diff --git a/sources/MVCFramework.Commons.pas b/sources/MVCFramework.Commons.pas index ff1be2ac..f7f222f7 100644 --- a/sources/MVCFramework.Commons.pas +++ b/sources/MVCFramework.Commons.pas @@ -663,7 +663,8 @@ uses IdCoder3to4, System.NetEncoding, System.Character, - MVCFramework.Serializer.JsonDataObjects, MVCFramework.Serializer.Commons; + MVCFramework.Serializer.JsonDataObjects, MVCFramework.Serializer.Commons, + System.RegularExpressions; var GlobalAppName, GlobalAppPath, GlobalAppExe: string; @@ -1392,6 +1393,7 @@ var lIsLowerCase: Boolean; lIsUpperCase, lPreviousWasUpperCase: Boolean; lIsAlpha: Boolean; + lIsNumber: Boolean; begin {TODO -oDanieleT -cGeneral : Make this function faster!} lNextUpCase := MakeFirstUpperToo; @@ -1403,8 +1405,9 @@ begin C := Value.Chars[I]; lIsLowerCase := CharInSet(C, ['a' .. 'z']); lIsUpperCase := CharInSet(C, ['A' .. 'Z']); + lIsNumber := CharInSet(C, ['0' .. '9']); lIsAlpha := lIsLowerCase or lIsUpperCase; - if not lIsAlpha then + if not (lIsAlpha or lIsNumber) then begin lNextUpCase := True; lPreviousWasUpperCase := False; @@ -1430,6 +1433,10 @@ begin end; end; lPreviousWasUpperCase := lIsUpperCase; + if lIsNumber then + begin + lNextUpCase := True; + end; end; Result := lSB.ToString; finally @@ -1444,9 +1451,16 @@ var C: Char; lIsUpperCase, lIsLowerCase, lLastWasLowercase: Boolean; lCanInsertAnUnderscore: Boolean; + lIsNumber: Boolean; + lLastWasUnderscore: Boolean; + lIsUnderscore: Boolean; + lLastWasNumber: Boolean; + lIsAlpha: Boolean; begin lCanInsertAnUnderscore := False; lLastWasLowercase := False; + lLastWasUnderscore := False; + lLastWasNumber := False; lSB := TStringBuilder.Create; try for I := 0 to Length(Value) - 1 do @@ -1454,8 +1468,13 @@ begin C := Value.Chars[I]; lIsUpperCase := CharInSet(C, ['A' .. 'Z']); lIsLowerCase := CharInSet(C, ['a' .. 'z']); - lCanInsertAnUnderscore := lCanInsertAnUnderscore and lLastWasLowercase; - if lIsUpperCase and (I > 0) and lCanInsertAnUnderscore then + lIsNumber := CharInSet(C, ['0' .. '9']); + lIsUnderscore := C = '_'; + lIsAlpha := lIsUpperCase or lIsLowerCase; + + lCanInsertAnUnderscore := lCanInsertAnUnderscore and (lLastWasLowercase or lLastWasNumber) and + (not lLastWasUnderscore); + if (lIsUpperCase or lIsNumber or (lIsAlpha and lLastWasNumber)) and (I > 0) and lCanInsertAnUnderscore then begin lSB.Append('_'); lCanInsertAnUnderscore := False; @@ -1464,8 +1483,13 @@ begin begin lCanInsertAnUnderscore := True; end; - lSB.Append(LowerCase(C)); + if not (lLastWasUnderscore and lIsUnderscore) then + begin + lSB.Append(LowerCase(C)); + end; + lLastWasUnderscore := lIsUnderscore; lLastWasLowercase := lIsLowerCase; + lLastWasNumber := lIsNumber; end; Result := lSB.ToString; finally diff --git a/sources/MVCFramework.Serializer.Commons.pas b/sources/MVCFramework.Serializer.Commons.pas index ebba76dd..d01fedcd 100644 --- a/sources/MVCFramework.Serializer.Commons.pas +++ b/sources/MVCFramework.Serializer.Commons.pas @@ -59,7 +59,7 @@ type TMVCSerializationType = (stUnknown, stDefault, stProperties, stFields); - TMVCNameCase = (ncAsIs, ncUpperCase, ncLowerCase, ncCamelCase, ncPascalCase); + TMVCNameCase = (ncAsIs, ncUpperCase, ncLowerCase, ncCamelCase, ncPascalCase, ncSnakeCase); TMVCDataType = (dtObject, dtArray); @@ -627,6 +627,10 @@ begin begin Result := CamelCase(Value, True); end; + ncSnakeCase: + begin + Result := SnakeCase(Value); + end; ncAsIs: begin Result := Value; diff --git a/unittests/general/Several/FrameworkTestsU.pas b/unittests/general/Several/FrameworkTestsU.pas index 4bd3d70d..84a4439e 100644 --- a/unittests/general/Several/FrameworkTestsU.pas +++ b/unittests/general/Several/FrameworkTestsU.pas @@ -199,8 +199,8 @@ type [TestFixture] TTestNameCase = class(TObject) private - fOutDATA: array [1 .. 4] of array [ncAsIs .. ncPascalCase] of string; - fOrigDATA: array [1 .. 4] of string; + fOutDATA: array [1 .. 5] of array [ncAsIs .. ncSnakeCase] of string; + fOrigDATA: array [1 .. 5] of string; public [SetupFixture] procedure SetupFixture; @@ -1790,35 +1790,48 @@ end; procedure TTestNameCase.SetupFixture; begin - fOrigDATA[1] := 'one_two'; + fOrigDATA[1] := 'one_two_3or4'; fOrigDATA[2] := 'ONE_TWO_THREE'; fOrigDATA[3] := 'JustOne'; fOrigDATA[4] := '_with__underscores_'; + fOrigDATA[5] := 'oneTwo___three04'; fOutDATA[1][ncAsIs] := fOrigDATA[1]; fOutDATA[2][ncAsIs] := fOrigDATA[2]; fOutDATA[3][ncAsIs] := fOrigDATA[3]; fOutDATA[4][ncAsIs] := fOrigDATA[4]; + fOutDATA[5][ncAsIs] := fOrigDATA[5]; - fOutDATA[1][ncUpperCase] := 'ONE_TWO'; + fOutDATA[1][ncUpperCase] := 'ONE_TWO_3OR4'; fOutDATA[2][ncUpperCase] := 'ONE_TWO_THREE'; fOutDATA[3][ncUpperCase] := 'JUSTONE'; fOutDATA[4][ncUpperCase] := '_WITH__UNDERSCORES_'; + fOutDATA[5][ncUpperCase] := 'ONETWO___THREE04'; - fOutDATA[1][ncLowerCase] := 'one_two'; + fOutDATA[1][ncLowerCase] := 'one_two_3or4'; fOutDATA[2][ncLowerCase] := 'one_two_three'; fOutDATA[3][ncLowerCase] := 'justone'; fOutDATA[4][ncLowerCase] := '_with__underscores_'; + fOutDATA[5][ncLowerCase] := 'onetwo___three04'; - fOutDATA[1][ncCamelCase] := 'oneTwo'; + fOutDATA[1][ncCamelCase] := 'oneTwo3Or4'; fOutDATA[2][ncCamelCase] := 'oneTwoThree'; fOutDATA[3][ncCamelCase] := 'justOne'; fOutDATA[4][ncCamelCase] := 'WithUnderscores'; + fOutDATA[5][ncCamelCase] := 'oneTwoThree04'; - fOutDATA[1][ncPascalCase] := 'OneTwo'; + fOutDATA[1][ncPascalCase] := 'OneTwo3Or4'; fOutDATA[2][ncPascalCase] := 'OneTwoThree'; fOutDATA[3][ncPascalCase] := 'JustOne'; fOutDATA[4][ncPascalCase] := 'WithUnderscores'; + fOutDATA[5][ncPascalCase] := 'OneTwoThree04'; + + fOutDATA[1][ncSnakeCase] := 'one_two_3_or_4'; + fOutDATA[2][ncSnakeCase] := 'one_two_three'; + fOutDATA[3][ncSnakeCase] := 'just_one'; + fOutDATA[4][ncSnakeCase] := '_with_underscores_'; + fOutDATA[5][ncSnakeCase] := 'one_two_three_04'; + end; procedure TTestNameCase.TestNameCase; @@ -1829,9 +1842,9 @@ var lOutData: string; lActualOutData: string; begin - for lNameCaseIdx := ncAsIs to ncPascalCase do + for lNameCaseIdx := ncAsIs to ncSnakeCase do begin - for I := 1 to 4 do + for I := 1 to 5 do begin lOrig := fOrigDATA[I]; lOutData := fOutDATA[I][lNameCaseIdx];