ALib C++ Library
Library Version: 2511 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
18 module ALib.Format.FormatterJavaStyle;
19 import ALib.Lang;
20 import ALib.Characters.Functions;
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 // arguments are counted starting with 1.
42
43 // set number format to Java defaults
44 DefaultNumberFormat.Flags -= NumberFormatFlags::ForceDecimalPoint;
45 DefaultNumberFormat.Flags += NumberFormatFlags::WriteExponentPlusSign;
46
47 // set number format to Java defaults
48 DefaultNumberFormat .HexLiteralPrefix = A_CHAR( "0X" );
49
50 DefaultNumberFormat .OctLiteralPrefix =
51 AlternativeNumberFormat.OctLiteralPrefix = A_CHAR( "0" );
52
53 DefaultNumberFormat .NANLiteral =
54 AlternativeNumberFormat.NANLiteral = A_CHAR( "NaN" );
55 DefaultNumberFormat .INFLiteral = A_CHAR( "INFINITY" );
56 AlternativeNumberFormat.INFLiteral = A_CHAR( "Infinity" );
57}
58
59
61 SPFormatter clone;
63
64 // create a clone of #Next, in the case that next is derived from std base class
65 if( Next )
66 clone->Next= Next->Clone();
67
68 // copy my settings, that's it
69 clone->CloneSettings( *this );
70
71 return clone;
72}
73
74
75
77 // first invoke parent's setting...
79
80 // ...then make some "Java like" adjustments
81 placeholderJS.ConversionUpper = false;
82 placeholder.ValueAlignment = lang::Alignment::Right;
83
84 placeholder.NF.Flags -= NumberFormatFlags::ForceDecimalPoint;
85 placeholder.NF.Flags -= NumberFormatFlags::OmitTrailingFractionalZeros;
86
87 placeholderJS.AlternateForm = false;
88
89 placeholderJS.Precision = -1;
90 placeholderJS.DefaultPrecision = 6;
91}
92
93
95 integer idx= 0;
96 while( (idx= parser.IndexOf('%', idx )) >= 0
97 && ( parser.CharAt( idx + 1 ) == '%'
98 || parser.CharAt( idx + 1 ) == 'n' ) )
99 {
100 // double ESC found (escaped %% or %n)
101 idx+= + 2;
102 }
103 return idx;
104}
105
106
108 if( length == 0)
109 return;
110
111 targetString->EnsureRemainingCapacity( length );
112 auto* src = parser.Buffer();
113 auto* dest= targetString->VBuffer() + targetString->Length();
114 parser.ConsumeChars( length );
115
116 character c1;
117 character c2= *src;
118 while( length > 1 ) {
119 c1= c2;
120 c2= *++src;
121
122 if( ( c1 == '%' && c2 =='%')
123 || c1 == '\\' )
124 {
125 if( c1 == '\\' )
126 switch(c2) {
127 case 'r': c1= '\r' ; break;
128 case 'n': c1= '\n' ; break;
129 case 't': c1= '\t' ; break;
130 case 'a': c1= '\a' ; break;
131 case 'b': c1= '\b' ; break;
132 case 'v': c1= '\v' ; break;
133 case 'f': c1= '\f' ; break;
134 case '"': c1= '"' ; break;
135 default: c1= '?' ; break;
136 }
137
138 c2= *++src;
139 --length;
140 }
141 else if( c1 == '%' && c2 =='n' ) {
142 c1= '\n';
143 ++src;
144 --length;
145 }
146
147 *dest++= c1;
148 --length;
149 }
150
151 // copy last character and adjust target string length:
152 // Note: length usually is 1. Only if last character is an escape sequence, it is 0.
153 if( length == 1)
154 *dest= *src;
155 targetString->SetLength( dest - targetString->VBuffer() + length);
156}
157
158
159
161 enum states
162 {
163 POSITION = 1,
164 FLAGS = 2,
165 WIDTH = 3,
166 PRECISION = 4,
167 TYPE = 5,
168 TYPE_SUFFIX = 6,
169 END = 10,
170 };
171
172 states state= POSITION;
173 #define NEXTSTATE(s) { state= s; continue; }
174
175 while( true ) {
176 // switch over state. With 'break' we consume one character (kind of success) while
177 // with 'continue' we keep the current character (and go to another state)
178 switch ( state ) {
179 case POSITION:
180 {
181 int argNo= -1;
182 if ( parser.ConsumeChar('<') ) {
183 argNo= placeholder.PreviousArgIdx + 1;
184 } else {
185 integer i= 0;
186 while( i < parser.Length()
187 && isdigit( parser.CharAt<NC>(i) ) )
188 ++i;
189
190 if( i > 0 && parser.CharAt<NC>(i) == '$') {
191 parser.ConsumeDecDigits( argNo ); ALIB_ASSERT_RESULT_EQUALS(
192 parser.ConsumeChar('$') , true )
193 } }
194 if( argNo >= 0 )
195 setArgument( argNo );
196
197 NEXTSTATE(FLAGS)
198 }
199
200 case FLAGS:
201 {
202 bool flagsDone= false;
203 while(!flagsDone) {
204 switch ( parser.CharAtStart() ) {
205 case '-':
206 placeholder.ValueAlignment= lang::Alignment::Left;
207 placeholder.AlignmentSpecified= true;
208 break;
209
210 case '^':
211 placeholder.ValueAlignment= lang::Alignment::Center;
212 placeholder.AlignmentSpecified= true;
213 break;
214
215 case '#':
216 {
217 placeholderJS.AlternateForm= true;
218 placeholder.WriteBinOctHexPrefix= true;
219 placeholder.NF.Flags += NumberFormatFlags::ForceDecimalPoint;
220 placeholder.NF.Flags -= NumberFormatFlags::OmitTrailingFractionalZeros;
221 break;
222 }
223
224 case '+':
225 case ' ':
226 placeholder.NF.PlusSign= parser.CharAtStart();
227 break;
228
229 case '0':
230 placeholder.SignPaddingMode= true;
231 break;
232
233 case ',':
234 placeholder.NF.Flags += NumberFormatFlags::WriteGroupChars;
235 break;
236
237 case '(':
239 formatString, formatString.Length() - parser.Length() );
240
241 default:
242 flagsDone= true;
243 break;
244 }
245
246 if( !flagsDone )
247 parser.ConsumeChars( 1 );
248 }
249 NEXTSTATE(WIDTH)
250 }
251
252
253 case WIDTH:
254 {
255 if( isdigit( parser.CharAtStart() ) )
256 parser.ConsumeDecDigits( placeholder.Width );
257
258 NEXTSTATE(PRECISION)
259 }
260
261 case PRECISION:
262 {
263 if( parser.ConsumeChar( '.' ) && !parser.ConsumeDecDigits( placeholderJS.Precision ) )
265 formatString, formatString.Length() - parser.Length() );
266
267 NEXTSTATE(TYPE)
268 }
269
270 case TYPE:
271 {
272 placeholder.TypeCode= parser.CharAtStart();
273 parser.ConsumeChars( 1 );
274
275 character typeCharLower= characters::ToLower( placeholder.TypeCode );
276
277 if ( typeCharLower == 'a' ) {
279 formatString, formatString.Length() - parser.Length() - 1 );
280 }
281
282 if( placeholderJS.AlternateForm && String( A_CHAR( "sSbBhHgGcCtT" ) ).IndexOf( placeholder.TypeCode ) >= 0 )
284 placeholder.TypeCode,
285 formatString, formatString.Length() - parser.Length() - 1 );
286
287 if( String(A_CHAR( "seg" )).IndexOf( placeholder.TypeCode ) >= 0 ) {
288 if( placeholder.TypeCode != 's' )
289 placeholder.NF.ExponentSeparator= AlternativeNumberFormat.ExponentSeparator;
290 placeholder.NF.INFLiteral= AlternativeNumberFormat.INFLiteral;
291 placeholder.NF.NANLiteral= AlternativeNumberFormat.NANLiteral;
292 }
293
294 if( String(A_CHAR( "SBCT" )).IndexOf( placeholder.TypeCode ) >= 0 )
295 placeholderJS.ConversionUpper= true;
296
297 if( NCString("egf").IndexOf( static_cast<nchar>(typeCharLower) ) < 0 )
298 placeholder.CutContent= placeholderJS.Precision;
299
300 if( placeholderJS.Precision >=0 && String(A_CHAR( "cCtTd" )).IndexOf( placeholder.TypeCode ) >= 0 )
302 placeholderJS.Precision, placeholder.TypeCode,
303 formatString, formatString.Length() - parser.Length() - 1 );
304
305 if( placeholder.TypeCode == 'X' || placeholder.TypeCode == 'H' ) placeholder.NF.Flags -= NumberFormatFlags::HexLowerCase;
306 else if( placeholder.TypeCode == 'x' || placeholder.TypeCode == 'h' ) placeholder.NF.HexLiteralPrefix= AlternativeNumberFormat.HexLiteralPrefix;
307
308
309 switch ( typeCharLower ) {
310 case 's': placeholderJS.Precision= -1;
311 break;
312
313 case 'b': placeholder.Type= PHTypes::Bool; break;
314 case 'c': placeholder.Type= PHTypes::Character; break;
315 case 'd': placeholder.Type= PHTypes::IntBase10; break;
316 case 'o': placeholder.Type= PHTypes::IntOctal; break;
317 case 'x': placeholder.Type= PHTypes::IntHex; break;
318 case 'h': placeholder.Type= PHTypes::IntHex; break;
319 case 'e': placeholder.Type= PHTypes::Float;
320 placeholder.NF.Flags+= NumberFormatFlags::ForceScientific; break;
321 case 'g': placeholder.Type= PHTypes::Float; break;
322 case 'f': placeholder.Type= PHTypes::Float;
323 placeholder.NF.IntegralPartMinimumWidth= 1;
324 placeholderJS.DefaultPrecision = -1; break;
325
326 case 't': placeholderJS.DateTime= parser.CharAtStart();
327 parser.ConsumeChars( 1 );
328 switch( placeholderJS.DateTime ) {
329 case 'H': placeholder.FormatSpec= A_CHAR( "HH" ) ; break;
330 case 'k': placeholder.FormatSpec= A_CHAR( "H" ) ; break;
331 case 'I': placeholder.FormatSpec= A_CHAR( "KK" ) ; break;
332 case 'l': placeholder.FormatSpec= A_CHAR( "K" ) ; break;
333 case 'M': placeholder.FormatSpec= A_CHAR( "mm" ) ; break;
334 case 'S': placeholder.FormatSpec= A_CHAR( "ss" ) ; break;
335 // not supported: case 'L': ;
336 // not supported: case 'N': ;
337 // not supported: case 'p': ;
338 // not supported: case 'z': ;
339 // not supported: case 'Z': ;
340 // not supported: case 's': ;
341 // not supported: case 'Q': ;
342 case 'B': placeholder.FormatSpec= A_CHAR( "MMMM" ); break;
343 case 'b': placeholder.FormatSpec= A_CHAR( "MMM" ); break;
344 case 'h':
345 case 'A': placeholder.FormatSpec= A_CHAR( "dddd" ); break;
346 case 'a': placeholder.FormatSpec= A_CHAR( "ddd" ); break;
347 // not supported: case 'C': ;
348 case 'Y': placeholder.FormatSpec= A_CHAR( "yyyy" ); break;
349 case 'y': placeholder.FormatSpec= A_CHAR( "yy" ); break;
350 // not supported: case 'j': ;
351 case 'm': placeholder.FormatSpec= A_CHAR( "MM" ); break;
352 case 'd': placeholder.FormatSpec= A_CHAR( "dd" ); break;
353 case 'e': placeholder.FormatSpec= A_CHAR( "d" ); break;
354
355 case 'R': placeholder.FormatSpec= A_CHAR( "HH:mm" ); break;
356 case 'T': placeholder.FormatSpec= A_CHAR( "HH:mm:ss" ); break;
357 // not supported: case 'r': ;
358
359 case 'D': placeholder.FormatSpec= A_CHAR( "MM/dd/yy" ); break;
360 case 'F': placeholder.FormatSpec= A_CHAR( "yyyy-MM-dd" ); break;
361 // not supported: case 'c': ;
362
363 default:
365 placeholderJS.DateTime,
366 formatString, formatString.Length() - parser.Length() - 1 );
367 }
368 break;
369
370 default:
372 placeholder.TypeCode,
373 formatString, formatString.Length() - parser.Length() - 1 );
374 }
375
376 NEXTSTATE(TYPE_SUFFIX)
377 }
378
379 case TYPE_SUFFIX:
380 {
381 NEXTSTATE(END)
382 }
383
384 case END:
385 //parser.ConsumeChars(1);
386 return true;
387
388 default: ALIB_ERROR( "FORMAT", "Illegal switch state." ) break;
389 } // state switch
390
391 } // read loop
392
393}
394
395
397 if( startIdx >= 0 && placeholderJS.ConversionUpper && target == nullptr )
398 targetString->ToUpper( startIdx );
399 return true;
400}
401
402
404 bool wasFloat= placeholder.Type == PHTypes::Float;
405 if( wasFloat ) {
406 if ( placeholderJS.Precision >= 0 )
407 placeholder.NF.FractionalPartWidth= placeholderJS.Precision;
408 else if( placeholder.NF.FractionalPartWidth < 0 )
409 placeholder.NF.FractionalPartWidth= placeholderJS.DefaultPrecision;
410 }
411
413
414 if( !wasFloat && placeholder.Type == PHTypes::Float ) {
415 if ( placeholderJS.Precision >= 0 )
416 placeholder.NF.FractionalPartWidth= placeholderJS.Precision;
417 }
418
419 return result;
420}
421
422} // namespace [alib::format]
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:1083
#define ALIB_CALLER_NULLED
Definition alib.inl:1027
#define A_CHAR(STR)
#define ALIB_ERROR(domain,...)
Definition alib.inl:1062
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:484
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.