ALib C++ Framework
by
Library Version: 2511 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
formatterjavastyle.cpp
1//##################################################################################################
2// ALib C++ Framework
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 "Configuration MACRO 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 case '\\': c1='\\' ; break;
136 default: c1= '?' ; break;
137 }
138
139 c2= *++src;
140 --length;
141 }
142 else if( c1 == '%' && c2 =='n' ) {
143 c1= '\n';
144 ++src;
145 --length;
146 }
147
148 *dest++= c1;
149 --length;
150 }
151
152 // copy the last character and adjust target string length:
153 // Note: length usually is 1. Only if last character is an escape sequence, it is 0.
154 if( length == 1)
155 *dest= *src;
156 targetString->SetLength( dest - targetString->VBuffer() + length);
157}
158
159
160
162 enum states
163 {
164 POSITION = 1,
165 FLAGS = 2,
166 WIDTH = 3,
167 PRECISION = 4,
168 TYPE = 5,
169 TYPE_SUFFIX = 6,
170 END = 10,
171 };
172
173 states state= POSITION;
174 #define NEXTSTATE(s) { state= s; continue; }
175
176 while( true ) {
177 // switch over state. With 'break' we consume one character (kind of success) while
178 // with 'continue' we keep the current character (and go to another state)
179 switch ( state ) {
180 case POSITION:
181 {
182 int argNo= -1;
183 if ( parser.ConsumeChar('<') ) {
184 argNo= placeholder.PreviousArgIdx + 1;
185 } else {
186 integer i= 0;
187 while( i < parser.Length()
188 && isdigit( parser.CharAt<NC>(i) ) )
189 ++i;
190
191 if( i > 0 && parser.CharAt<NC>(i) == '$') {
192 parser.ConsumeDecDigits( argNo ); ALIB_ASSERT_RESULT_EQUALS(
193 parser.ConsumeChar('$') , true )
194 } }
195 if( argNo >= 0 )
196 setArgument( argNo );
197
198 NEXTSTATE(FLAGS)
199 }
200
201 case FLAGS:
202 {
203 bool flagsDone= false;
204 while(!flagsDone) {
205 switch ( parser.CharAtStart() ) {
206 case '-':
207 placeholder.ValueAlignment= lang::Alignment::Left;
208 placeholder.AlignmentSpecified= true;
209 break;
210
211 case '^':
212 placeholder.ValueAlignment= lang::Alignment::Center;
213 placeholder.AlignmentSpecified= true;
214 break;
215
216 case '#':
217 {
218 placeholderJS.AlternateForm= true;
219 placeholder.WriteBinOctHexPrefix= true;
220 placeholder.NF.Flags += NumberFormatFlags::ForceDecimalPoint;
221 placeholder.NF.Flags -= NumberFormatFlags::OmitTrailingFractionalZeros;
222 break;
223 }
224
225 case '+':
226 case ' ':
227 placeholder.NF.PlusSign= parser.CharAtStart();
228 break;
229
230 case '0':
231 placeholder.SignPaddingMode= true;
232 break;
233
234 case ',':
235 placeholder.NF.Flags += NumberFormatFlags::WriteGroupChars;
236 break;
237
238 case '(':
240 formatString, formatString.Length() - parser.Length() );
241
242 default:
243 flagsDone= true;
244 break;
245 }
246
247 if( !flagsDone )
248 parser.ConsumeChars( 1 );
249 }
250 NEXTSTATE(WIDTH)
251 }
252
253
254 case WIDTH:
255 {
256 if( isdigit( parser.CharAtStart() ) )
257 parser.ConsumeDecDigits( placeholder.Width );
258
259 NEXTSTATE(PRECISION)
260 }
261
262 case PRECISION:
263 {
264 if( parser.ConsumeChar( '.' ) && !parser.ConsumeDecDigits( placeholderJS.Precision ) )
266 formatString, formatString.Length() - parser.Length() );
267
268 NEXTSTATE(TYPE)
269 }
270
271 case TYPE:
272 {
273 placeholder.TypeCode= parser.CharAtStart();
274 parser.ConsumeChars( 1 );
275
276 character typeCharLower= characters::ToLower( placeholder.TypeCode );
277
278 if ( typeCharLower == 'a' ) {
280 formatString, formatString.Length() - parser.Length() - 1 );
281 }
282
283 if( placeholderJS.AlternateForm && String( A_CHAR( "sSbBhHgGcCtT" ) ).IndexOf( placeholder.TypeCode ) >= 0 )
285 placeholder.TypeCode,
286 formatString, formatString.Length() - parser.Length() - 1 );
287
288 if( String(A_CHAR( "seg" )).IndexOf( placeholder.TypeCode ) >= 0 ) {
289 if( placeholder.TypeCode != 's' )
290 placeholder.NF.ExponentSeparator= AlternativeNumberFormat.ExponentSeparator;
291 placeholder.NF.INFLiteral= AlternativeNumberFormat.INFLiteral;
292 placeholder.NF.NANLiteral= AlternativeNumberFormat.NANLiteral;
293 }
294
295 if( String(A_CHAR( "SBCT" )).IndexOf( placeholder.TypeCode ) >= 0 )
296 placeholderJS.ConversionUpper= true;
297
298 if( NCString("egf").IndexOf( static_cast<nchar>(typeCharLower) ) < 0 )
299 placeholder.CutContent= placeholderJS.Precision;
300
301 if( placeholderJS.Precision >=0 && String(A_CHAR( "cCtTd" )).IndexOf( placeholder.TypeCode ) >= 0 )
303 placeholderJS.Precision, placeholder.TypeCode,
304 formatString, formatString.Length() - parser.Length() - 1 );
305
306 if( placeholder.TypeCode == 'X' || placeholder.TypeCode == 'H' ) placeholder.NF.Flags -= NumberFormatFlags::HexLowerCase;
307 else if( placeholder.TypeCode == 'x' || placeholder.TypeCode == 'h' ) placeholder.NF.HexLiteralPrefix= AlternativeNumberFormat.HexLiteralPrefix;
308
309
310 switch ( typeCharLower ) {
311 case 's': placeholderJS.Precision= -1;
312 break;
313
314 case 'b': placeholder.Type= PHTypes::Bool; break;
315 case 'c': placeholder.Type= PHTypes::Character; break;
316 case 'd': placeholder.Type= PHTypes::IntBase10; break;
317 case 'o': placeholder.Type= PHTypes::IntOctal; break;
318 case 'x': placeholder.Type= PHTypes::IntHex; break;
319 case 'h': placeholder.Type= PHTypes::IntHex; break;
320 case 'e': placeholder.Type= PHTypes::Float;
321 placeholder.NF.Flags+= NumberFormatFlags::ForceScientific; break;
322 case 'g': placeholder.Type= PHTypes::Float; break;
323 case 'f': placeholder.Type= PHTypes::Float;
324 placeholder.NF.IntegralPartMinimumWidth= 1;
325 placeholderJS.DefaultPrecision = -1; break;
326
327 case 't': placeholderJS.DateTime= parser.CharAtStart();
328 parser.ConsumeChars( 1 );
329 switch( placeholderJS.DateTime ) {
330 case 'H': placeholder.FormatSpec= A_CHAR( "HH" ) ; break;
331 case 'k': placeholder.FormatSpec= A_CHAR( "H" ) ; break;
332 case 'I': placeholder.FormatSpec= A_CHAR( "KK" ) ; break;
333 case 'l': placeholder.FormatSpec= A_CHAR( "K" ) ; break;
334 case 'M': placeholder.FormatSpec= A_CHAR( "mm" ) ; break;
335 case 'S': placeholder.FormatSpec= A_CHAR( "ss" ) ; break;
336 // not supported: case 'L': ;
337 // not supported: case 'N': ;
338 // not supported: case 'p': ;
339 // not supported: case 'z': ;
340 // not supported: case 'Z': ;
341 // not supported: case 's': ;
342 // not supported: case 'Q': ;
343 case 'B': placeholder.FormatSpec= A_CHAR( "MMMM" ); break;
344 case 'b': placeholder.FormatSpec= A_CHAR( "MMM" ); break;
345 case 'h':
346 case 'A': placeholder.FormatSpec= A_CHAR( "dddd" ); break;
347 case 'a': placeholder.FormatSpec= A_CHAR( "ddd" ); break;
348 // not supported: case 'C': ;
349 case 'Y': placeholder.FormatSpec= A_CHAR( "yyyy" ); break;
350 case 'y': placeholder.FormatSpec= A_CHAR( "yy" ); break;
351 // not supported: case 'j': ;
352 case 'm': placeholder.FormatSpec= A_CHAR( "MM" ); break;
353 case 'd': placeholder.FormatSpec= A_CHAR( "dd" ); break;
354 case 'e': placeholder.FormatSpec= A_CHAR( "d" ); break;
355
356 case 'R': placeholder.FormatSpec= A_CHAR( "HH:mm" ); break;
357 case 'T': placeholder.FormatSpec= A_CHAR( "HH:mm:ss" ); break;
358 // not supported: case 'r': ;
359
360 case 'D': placeholder.FormatSpec= A_CHAR( "MM/dd/yy" ); break;
361 case 'F': placeholder.FormatSpec= A_CHAR( "yyyy-MM-dd" ); break;
362 // not supported: case 'c': ;
363
364 default:
366 placeholderJS.DateTime,
367 formatString, formatString.Length() - parser.Length() - 1 );
368 }
369 break;
370
371 default:
373 placeholder.TypeCode,
374 formatString, formatString.Length() - parser.Length() - 1 );
375 }
376
377 NEXTSTATE(TYPE_SUFFIX)
378 }
379
380 case TYPE_SUFFIX:
381 {
382 NEXTSTATE(END)
383 }
384
385 case END:
386 //parser.ConsumeChars(1);
387 return true;
388
389 default: ALIB_ERROR( "FORMAT", "Illegal switch state." ) break;
390 } // state switch
391
392 } // read loop
393
394}
395
396
398 if( startIdx >= 0 && placeholderJS.ConversionUpper && target == nullptr )
399 targetString->ToUpper( startIdx );
400 return true;
401}
402
403
405 bool wasFloat= placeholder.Type == PHTypes::Float;
406 if( wasFloat ) {
407 if ( placeholderJS.Precision >= 0 )
408 placeholder.NF.FractionalPartWidth= placeholderJS.Precision;
409 else if( placeholder.NF.FractionalPartWidth < 0 )
410 placeholder.NF.FractionalPartWidth= placeholderJS.DefaultPrecision;
411 }
412
414
415 if( !wasFloat && placeholder.Type == PHTypes::Float ) {
416 if ( placeholderJS.Precision >= 0 )
417 placeholder.NF.FractionalPartWidth= placeholderJS.Precision;
418 }
419
420 return result;
421}
422
423} // namespace [alib::format]
#define ALIB_ASSERT_RESULT_EQUALS( func, value)
Definition alib.inl:1161
#define ALIB_CALLER_NULLED
Definition alib.inl:1105
#define A_CHAR(STR)
Definition alib.inl:1325
#define ALIB_ERROR(domain,...)
Definition alib.inl:1140
void InsertDerived(TArgs &&... args)
virtual void writeStringPortion(integer length) override
virtual void resetPlaceholder() override
virtual SPFormatter Clone() override
PlaceholderAttributesJS placeholderJS
The extended placeholder attributes.
virtual bool checkStdFieldAgainstArgument() override
virtual integer findPlaceholder() override
virtual bool parsePlaceholder() override
virtual 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
virtual void CloneSettings(Formatter &reference)
NumberFormat AlternativeNumberFormat
SharedPtr< Formatter > Next
TChar ToLower(TChar c)
@ Center
Chooses centered alignment.
@ Right
Chooses right alignment.
@ Left
Chooses left alignment.
containers::SharedPtr< format::Formatter > SPFormatter
Definition formatter.inl:42
strings::TCString< nchar > NCString
Type alias in namespace alib.
Definition cstring.inl:408
lang::integer integer
Type alias in namespace alib.
Definition integers.inl:149
strings::TString< character > String
Type alias in namespace alib.
Definition string.inl:2172
characters::nchar nchar
Type alias in namespace alib.
exceptions::Exception Exception
Type alias in namespace alib.
strings::TAString< character, lang::HeapAllocator > AString
Type alias in namespace alib.
characters::character character
Type alias in namespace alib.