From 7ebccfe1711587ee64e23fedf289010fc2616378 Mon Sep 17 00:00:00 2001 From: Guilherme Branco Stracini Date: Fri, 24 Jul 2020 07:25:03 -0300 Subject: [PATCH 1/3] add log consumer trace methods to log consumer class --- .../Log/Adapters/RollingTextFileLogAdapter.cs | 2 +- Src/CrispyWaffle/Log/LogConsumer.cs | 38 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/Src/CrispyWaffle/Log/Adapters/RollingTextFileLogAdapter.cs b/Src/CrispyWaffle/Log/Adapters/RollingTextFileLogAdapter.cs index e4d79508..62cc99f9 100644 --- a/Src/CrispyWaffle/Log/Adapters/RollingTextFileLogAdapter.cs +++ b/Src/CrispyWaffle/Log/Adapters/RollingTextFileLogAdapter.cs @@ -3,7 +3,7 @@ using Serialization; using System; - //TODO + //TODO issue #37 /// /// Class RollingTextFileLogAdapter. This class cannot be inherited. /// Implements the diff --git a/Src/CrispyWaffle/Log/LogConsumer.cs b/Src/CrispyWaffle/Log/LogConsumer.cs index 652a34fa..12c1d795 100644 --- a/Src/CrispyWaffle/Log/LogConsumer.cs +++ b/Src/CrispyWaffle/Log/LogConsumer.cs @@ -334,6 +334,44 @@ public static void Trace(string message, params object[] arguments) Trace(string.Format(message, arguments)); } + /// + /// Traces the specified exception. + /// + /// The exception. + /// The message. + public static void Trace(Exception exception, string message) + { + var category = GetCategory(); + + foreach (var provider in Providers.Where(p => + Filters.All(f => f.Filter(p.GetType().FullName, LogLevel.TRACE, category, message)))) + provider.Trace(category, message, exception); + } + + /// + /// Traces the specified exception. + /// + /// The exception. + /// The message. + /// The arguments. + public static void Trace(Exception exception, string message, params object[] arguments) + { + Trace(exception, string.Format(message, arguments)); + } + + /// + /// Traces the specified exception. + /// + /// The exception. + public static void Trace(Exception exception) + { + var category = GetCategory(); + + foreach (var provider in Providers.Where(p => + Filters.All(f => f.Filter(p.GetType().FullName, LogLevel.TRACE, category, exception.Message)))) + provider.Trace(category, exception); + } + /// /// Logs the message with info log level /// From 68d347208b920a48a816cb92d29e345f943ea9df Mon Sep 17 00:00:00 2001 From: Guilherme Branco Stracini Date: Fri, 24 Jul 2020 08:23:09 -0300 Subject: [PATCH 2/3] Implement EventLogProvider and EventLogAdapter as #41 --- README.md | 146 ++++++++++---------- Src/CrispyWaffle/CrispyWaffle.csproj | 63 ++++----- appveyor.yml | 194 +++++++++++++-------------- 3 files changed, 205 insertions(+), 198 deletions(-) diff --git a/README.md b/README.md index 7bcf9ae7..618f451a 100644 --- a/README.md +++ b/README.md @@ -1,70 +1,76 @@ -# CrispyWaffle - -The CrispyWaffle project - a toolkit for dotNet (both Core & Framework) projects - -![Crispy Waffle logo](https://raw.githubusercontent.com/guibranco/CrispyWaffle/master/logo.png) - -## CI/CD - -[![Build status](https://ci.appveyor.com/api/projects/status/dr93gad0na076ng3?svg=true)](https://ci.appveyor.com/project/guibranco/crispywaffle) -[![GitHub last commit](https://img.shields.io/github/last-commit/guibranco/CrispyWaffle)](https://github.com/guibranco/CrispyWaffle) -[![GitHub license](https://img.shields.io/github/license/guibranco/CrispyWaffle)](https://github.com/guibranco/CrispyWaffle) -[![Time tracker](https://wakatime.com/badge/github/guibranco/CrispyWaffle.svg)](https://wakatime.com/badge/github/guibranco/CrispyWaffle) - -## Code Quality - -[![Codacy Badge](https://api.codacy.com/project/badge/Grade/f9e814c726bb4ccb8b0380b1fd882f4b)](https://www.codacy.com/manual/guilherme_9/CrispyWaffle?utm_source=github.com&utm_medium=referral&utm_content=guibranco/CrispyWaffle&utm_campaign=Badge_Grade) -[![codecov](https://codecov.io/gh/guibranco/CrispyWaffle/branch/master/graph/badge.svg)](https://codecov.io/gh/guibranco/CrispyWaffle) -[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=guibranco_CrispyWaffle&metric=alert_status)](https://sonarcloud.io/dashboard?id=guibranco_CrispyWaffle) -[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=guibranco_CrispyWaffle&metric=coverage)](https://sonarcloud.io/dashboard?id=guibranco_CrispyWaffle) - -[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=guibranco_CrispyWaffle&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=guibranco_CrispyWaffle) -[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=guibranco_CrispyWaffle&metric=ncloc)](https://sonarcloud.io/dashboard?id=guibranco_CrispyWaffle) -[![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=guibranco_CrispyWaffle&metric=sqale_index)](https://sonarcloud.io/dashboard?id=guibranco_CrispyWaffle) -[![Duplicated Lines (%)](https://sonarcloud.io/api/project_badges/measure?project=guibranco_CrispyWaffle&metric=duplicated_lines_density)](https://sonarcloud.io/dashboard?id=guibranco_CrispyWaffle) - -[![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=guibranco_CrispyWaffle&metric=reliability_rating)](https://sonarcloud.io/dashboard?id=guibranco_CrispyWaffle) -[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=guibranco_CrispyWaffle&metric=security_rating)](https://sonarcloud.io/dashboard?id=guibranco_CrispyWaffle) -[![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=guibranco_CrispyWaffle&metric=code_smells)](https://sonarcloud.io/dashboard?id=guibranco_CrispyWaffle) -[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=guibranco_CrispyWaffle&metric=bugs)](https://sonarcloud.io/dashboard?id=guibranco_CrispyWaffle) -[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=guibranco_CrispyWaffle&metric=vulnerabilities)](https://sonarcloud.io/dashboard?id=guibranco_CrispyWaffle) - ---- - -## Installation - -[![GitHub last release](https://img.shields.io/github/release-date/guibranco/CrispyWaffle.svg?style=flat)](https://github.com/guibranco/CrispyWaffle) [![Github All Releases](https://img.shields.io/github/downloads/guibranco/CrispyWaffle/total.svg?style=flat)](https://github.com/guibranco/CrispyWaffle) - -Download the latest zip file from the [Release](https://github.com/GuiBranco/CrispyWaffle/releases) pages. - -| Package | Version | Downloads | -|------------------|:-------:|:-------:| -| **CrispyWaffle** | [![CrispyWaffle NuGet Version](https://img.shields.io/nuget/v/CrispyWaffle.svg?style=flat)](https://www.nuget.org/packages/CrispyWaffle/) | [![CrispyWaffle NuGet Downloads](https://img.shields.io/nuget/dt/CrispyWaffle.svg?style=flat)](https://www.nuget.org/packages/CrispyWaffle/) | - -NuGet installation via *Package Manager Console*: - -```ps -Install-Package CrispyWaffle -``` - ---- - -## Changelog - -2020-03-28 - Version 1.2 - [@guibranco](https://github.com/guibranco) - -- Add some unit tests. -- Update appveyor.yml with build enhancements and test coverages. -- Update readme template. - -2019-09-27 - Version 1.1 - [@guibranco](https://github.com/guibranco) - -- Add Math Extensions (CrispyWaffle.Extensions.MathExtensions namespace). -- Add Personal Data Validations (CrispyWaffle.Validations.PersonalDataValidations). -- Rename method *FormatDocument* to *FormatBrazilianDocument* (CrispyWaffle.Extensions.ConversionExtensions). -- Rename method *ParsePhoneNumber* to *arseBrazilianPhoneNumber* (CrispyWaffle.Extensions.ConversionExtensions). -- Removed *CleanListItems* (CrispyWaffle.Extensions.ConversionExtensions). -- Rename method *TryParsePhoneNumber* to *TryParseBrazilianPhoneNumber* (CrispyWaffle.Extensions.ConversionExtensions). -- Removed *CleanListItems* and *ToListString* (CrispyWaffle.Extensions.ConversionExtensions) **(Specific to application patterns)**. - ---- +# CrispyWaffle + +The CrispyWaffle project - a toolkit for dotNet (both Core & Framework) projects + +![Crispy Waffle logo](https://raw.githubusercontent.com/guibranco/CrispyWaffle/master/logo.png) + +## CI/CD + +[![Build status](https://ci.appveyor.com/api/projects/status/dr93gad0na076ng3?svg=true)](https://ci.appveyor.com/project/guibranco/crispywaffle) +[![GitHub last commit](https://img.shields.io/github/last-commit/guibranco/CrispyWaffle)](https://github.com/guibranco/CrispyWaffle) +[![GitHub license](https://img.shields.io/github/license/guibranco/CrispyWaffle)](https://github.com/guibranco/CrispyWaffle) +[![Time tracker](https://wakatime.com/badge/github/guibranco/CrispyWaffle.svg)](https://wakatime.com/badge/github/guibranco/CrispyWaffle) + +## Code Quality + +[![Codacy Badge](https://api.codacy.com/project/badge/Grade/f9e814c726bb4ccb8b0380b1fd882f4b)](https://www.codacy.com/manual/guilherme_9/CrispyWaffle?utm_source=github.com&utm_medium=referral&utm_content=guibranco/CrispyWaffle&utm_campaign=Badge_Grade) +[![codecov](https://codecov.io/gh/guibranco/CrispyWaffle/branch/master/graph/badge.svg)](https://codecov.io/gh/guibranco/CrispyWaffle) +[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=guibranco_CrispyWaffle&metric=alert_status)](https://sonarcloud.io/dashboard?id=guibranco_CrispyWaffle) +[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=guibranco_CrispyWaffle&metric=coverage)](https://sonarcloud.io/dashboard?id=guibranco_CrispyWaffle) + +[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=guibranco_CrispyWaffle&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=guibranco_CrispyWaffle) +[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=guibranco_CrispyWaffle&metric=ncloc)](https://sonarcloud.io/dashboard?id=guibranco_CrispyWaffle) +[![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=guibranco_CrispyWaffle&metric=sqale_index)](https://sonarcloud.io/dashboard?id=guibranco_CrispyWaffle) +[![Duplicated Lines (%)](https://sonarcloud.io/api/project_badges/measure?project=guibranco_CrispyWaffle&metric=duplicated_lines_density)](https://sonarcloud.io/dashboard?id=guibranco_CrispyWaffle) + +[![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=guibranco_CrispyWaffle&metric=reliability_rating)](https://sonarcloud.io/dashboard?id=guibranco_CrispyWaffle) +[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=guibranco_CrispyWaffle&metric=security_rating)](https://sonarcloud.io/dashboard?id=guibranco_CrispyWaffle) +[![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=guibranco_CrispyWaffle&metric=code_smells)](https://sonarcloud.io/dashboard?id=guibranco_CrispyWaffle) +[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=guibranco_CrispyWaffle&metric=bugs)](https://sonarcloud.io/dashboard?id=guibranco_CrispyWaffle) +[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=guibranco_CrispyWaffle&metric=vulnerabilities)](https://sonarcloud.io/dashboard?id=guibranco_CrispyWaffle) + +--- + +## Installation + +[![GitHub last release](https://img.shields.io/github/release-date/guibranco/CrispyWaffle.svg?style=flat)](https://github.com/guibranco/CrispyWaffle) [![Github All Releases](https://img.shields.io/github/downloads/guibranco/CrispyWaffle/total.svg?style=flat)](https://github.com/guibranco/CrispyWaffle) + +Download the latest zip file from the [Release](https://github.com/GuiBranco/CrispyWaffle/releases) pages. + +| Package | Version | Downloads | +|------------------|:-------:|:-------:| +| **CrispyWaffle** | [![CrispyWaffle NuGet Version](https://img.shields.io/nuget/v/CrispyWaffle.svg?style=flat)](https://www.nuget.org/packages/CrispyWaffle/) | [![CrispyWaffle NuGet Downloads](https://img.shields.io/nuget/dt/CrispyWaffle.svg?style=flat)](https://www.nuget.org/packages/CrispyWaffle/) | + +NuGet installation via *Package Manager Console*: + +```ps +Install-Package CrispyWaffle +``` + +--- + +## Changelog + +2020-07-24 - Version 1.3 - [@guibranco](https://github.com/guibranco) + +- Add EvenLogProvider and EventLogAdapter +- Add log Trace methods that support exceptions +- Add Fatal log level + +2020-03-28 - Version 1.2 - [@guibranco](https://github.com/guibranco) + +- Add some unit tests. +- Update appveyor.yml with build enhancements and test coverages. +- Update readme template. + +2019-09-27 - Version 1.1 - [@guibranco](https://github.com/guibranco) + +- Add Math Extensions (CrispyWaffle.Extensions.MathExtensions namespace). +- Add Personal Data Validations (CrispyWaffle.Validations.PersonalDataValidations). +- Rename method *FormatDocument* to *FormatBrazilianDocument* (CrispyWaffle.Extensions.ConversionExtensions). +- Rename method *ParsePhoneNumber* to *arseBrazilianPhoneNumber* (CrispyWaffle.Extensions.ConversionExtensions). +- Removed *CleanListItems* (CrispyWaffle.Extensions.ConversionExtensions). +- Rename method *TryParsePhoneNumber* to *TryParseBrazilianPhoneNumber* (CrispyWaffle.Extensions.ConversionExtensions). +- Removed *CleanListItems* and *ToListString* (CrispyWaffle.Extensions.ConversionExtensions) **(Specific to application patterns)**. + +--- diff --git a/Src/CrispyWaffle/CrispyWaffle.csproj b/Src/CrispyWaffle/CrispyWaffle.csproj index eb4393f8..58bae83d 100644 --- a/Src/CrispyWaffle/CrispyWaffle.csproj +++ b/Src/CrispyWaffle/CrispyWaffle.csproj @@ -1,32 +1,33 @@ - - - - 9A19103F-16F7-4668-BE54-9A1E7A4F7556 - netstandard2.0;netstandard2.1 - true - Guilherme Branco Stracini - © 2020 Guilherme Branco Stracini. All rights reserved. - The CrispyWaffle toolkit for .NET projects - https://guibranco.github.io/CrispyWaffle - https://github.com/guibranco/CrispyWaffle - GIT - toolkit sdk framework library lib - https://raw.githubusercontent.com/guibranco/CrispyWaffle/master/logo.png - logo.png - 1.0.0 - 1.0.0.0 - LICENSE - Guilherme Branco Stracini ME - CancellationToken support in ServiceLocator - - - - - - - - - - - + + + + 9A19103F-16F7-4668-BE54-9A1E7A4F7556 + netstandard2.0;netstandard2.1 + true + Guilherme Branco Stracini + © 2020 Guilherme Branco Stracini. All rights reserved. + The CrispyWaffle toolkit for .NET projects + https://guibranco.github.io/CrispyWaffle + https://github.com/guibranco/CrispyWaffle + GIT + toolkit sdk framework library lib + https://raw.githubusercontent.com/guibranco/CrispyWaffle/master/logo.png + logo.png + 1.0.0 + 1.0.0.0 + LICENSE + Guilherme Branco Stracini ME + CancellationToken support in ServiceLocator + + + + + + + + + + + + \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml index 5b29c64f..ef0b1338 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,97 +1,97 @@ -version: 1.2.{build} -skip_tags: true -image: Visual Studio 2019 -configuration: Release -skip_commits: - message: /(Create|Update) .*\.(png|jpg|jpeg|bmp|gif|md)/ - -environment: - SOLUTION_NAME: CrispyWaffle - SONAR_TOKEN: - secure: xLias5AyRmfrqKEmZIn9+wbX1ynwrdfkQJFNrLb0RWIdYPuVQgvrfCFHNgu5bqfw - GITHUB_TOKEN: - secure: VgYYJdvNLy/n9/uxxlaF0bMpIIrVxCb+dGr66U9nWfPWSN2ySdfuilO8klAw0uvF - CODACY_PROJECT_TOKEN: - secure: OTuCYXcGOH+xLcFFvPK9b0s1/vaDER5vKECPBk57azM+k9DQPcjKB7N70Wbsvfgm - -dotnet_csproj: - patch: true - file: '**\*.csproj' - version: '{version}' - package_version: '{version}' - assembly_version: '{version}' - file_version: '{version}' - informational_version: '{version}' - -before_build: -- ps: $env:SONAR_PROJECT = "$env:APPVEYOR_REPO_NAME" -replace "/","_" -- ps: $env:SONAR_ORGANIZATION = "$env:APPVEYOR_REPO_NAME" -replace "/.*$","-github" -- cmd: nuget restore -- cmd: choco install opencover.portable -- cmd: choco install codecov -- cmd: curl -L https://github.com/codacy/codacy-coverage-reporter/releases/download/7.7.0/codacy-coverage-reporter-assembly-7.7.0.jar > ./codacy-test-reporter.jar -- cmd: dotnet tool install --global dotnet-sonarscanner - -build: - publish_nuget: true - include_nuget_references: true - parallel: true - verbosity: normal - -build_script: -- ps: 'if (-Not $env:APPVEYOR_PULL_REQUEST_NUMBER -And $env:APPVEYOR_REPO_BRANCH -eq "master") { & - dotnet sonarscanner begin - /k:"$env:SONAR_PROJECT" - /o:"$env:SONAR_ORGANIZATION" - /d:sonar.host.url="https://sonarcloud.io" - /d:sonar.login="$env:SONAR_TOKEN" - /v:"$env:APPVEYOR_BUILD_NUMBER" - /d:sonar.exclusions="**/bin/**/*,**/obj/**/*" - /d:sonar.coverage.exclusions="**/$env:SOLUTION_NAME.Tests/**,**/*Tests.cs" - /d:sonar.cs.opencover.reportsPaths="$env:CD\Tests\$env:SOLUTION_NAME.Tests\coverage.opencover.xml" }' -- dotnet build %SOLUTION_NAME%.sln -- dotnet test .\Tests\%SOLUTION_NAME%.Tests\%SOLUTION_NAME%.Tests.csproj - /p:CollectCoverage=true - /p:CoverletOutputFormat="opencover" -- codecov -f "%CD%\Tests\%SOLUTION_NAME%.Tests\coverage.opencover.xml" -- java - -jar - ./codacy-test-reporter.jar report - -l CSharp - -t %CODACY_PROJECT_TOKEN% - -r "%CD%\Tests\%SOLUTION_NAME%.Tests\coverage.opencover.xml" -- ps: 'if (-Not $env:APPVEYOR_PULL_REQUEST_NUMBER -And $env:APPVEYOR_REPO_BRANCH -eq "master") { & - dotnet sonarscanner end - /d:sonar.login="$env:SONAR_TOKEN" }' - -after_build: -- xcopy %CD%\Src\%SOLUTION_NAME%\bin\Release\netstandard2.0\*.* %CD%\Build\ -- copy %CD%\Src\%SOLUTION_NAME%\bin\Release\%SOLUTION_NAME%.%APPVEYOR_BUILD_VERSION%.nupkg %SOLUTION_NAME%.%APPVEYOR_BUILD_VERSION%.nupkg -- rd /s /q %CD%\Src\%SOLUTION_NAME%\bin\Release\ -- xcopy %CD%\Tests\%SOLUTION_NAME%.Tests\*.xml %CD%\Coverage\ -- xcopy %CD%\Tests\%SOLUTION_NAME%.Tests\*.json %CD%\Coverage\ -- cd %CD% -- 7z a -tzip -mx9 "%SOLUTION_NAME%.%APPVEYOR_BUILD_VERSION%.zip" Build -- 7z a -tzip -mx9 "%SOLUTION_NAME%.%APPVEYOR_BUILD_VERSION%.Coverage.zip" Coverage - -artifacts: -- path: $(SOLUTION_NAME).%APPVEYOR_BUILD_VERSION%.zip - name: ZipFile -- path: $(SOLUTION_NAME).%APPVEYOR_BUILD_VERSION%.nupkg - name: Package -- path: $(SOLUTION_NAME).%APPVEYOR_BUILD_VERSION%.Coverage.zip - name: Coverage - -deploy: -- provider: Environment - name: NuGet - on: - branch: master - artifact: Package -- provider: GitHub - on: - branch: master - tag: $(appveyor_build_version) - auth_token: $(GITHUB_TOKEN) - force_update: true - artifact: ZipFile +version: 1.3.{build} +skip_tags: true +image: Visual Studio 2019 +configuration: Release +skip_commits: + message: /(Create|Update) .*\.(png|jpg|jpeg|bmp|gif|md)/ + +environment: + SOLUTION_NAME: CrispyWaffle + SONAR_TOKEN: + secure: xLias5AyRmfrqKEmZIn9+wbX1ynwrdfkQJFNrLb0RWIdYPuVQgvrfCFHNgu5bqfw + GITHUB_TOKEN: + secure: VgYYJdvNLy/n9/uxxlaF0bMpIIrVxCb+dGr66U9nWfPWSN2ySdfuilO8klAw0uvF + CODACY_PROJECT_TOKEN: + secure: OTuCYXcGOH+xLcFFvPK9b0s1/vaDER5vKECPBk57azM+k9DQPcjKB7N70Wbsvfgm + +dotnet_csproj: + patch: true + file: '**\*.csproj' + version: '{version}' + package_version: '{version}' + assembly_version: '{version}' + file_version: '{version}' + informational_version: '{version}' + +before_build: +- ps: $env:SONAR_PROJECT = "$env:APPVEYOR_REPO_NAME" -replace "/","_" +- ps: $env:SONAR_ORGANIZATION = "$env:APPVEYOR_REPO_NAME" -replace "/.*$","-github" +- cmd: nuget restore +- cmd: choco install opencover.portable +- cmd: choco install codecov +- cmd: curl -L https://github.com/codacy/codacy-coverage-reporter/releases/download/7.7.0/codacy-coverage-reporter-assembly-7.7.0.jar > ./codacy-test-reporter.jar +- cmd: dotnet tool install --global dotnet-sonarscanner + +build: + publish_nuget: true + include_nuget_references: true + parallel: true + verbosity: normal + +build_script: +- ps: 'if (-Not $env:APPVEYOR_PULL_REQUEST_NUMBER -And $env:APPVEYOR_REPO_BRANCH -eq "master") { & + dotnet sonarscanner begin + /k:"$env:SONAR_PROJECT" + /o:"$env:SONAR_ORGANIZATION" + /d:sonar.host.url="https://sonarcloud.io" + /d:sonar.login="$env:SONAR_TOKEN" + /v:"$env:APPVEYOR_BUILD_NUMBER" + /d:sonar.exclusions="**/bin/**/*,**/obj/**/*" + /d:sonar.coverage.exclusions="**/$env:SOLUTION_NAME.Tests/**,**/*Tests.cs" + /d:sonar.cs.opencover.reportsPaths="$env:CD\Tests\$env:SOLUTION_NAME.Tests\coverage.opencover.xml" }' +- dotnet build %SOLUTION_NAME%.sln +- dotnet test .\Tests\%SOLUTION_NAME%.Tests\%SOLUTION_NAME%.Tests.csproj + /p:CollectCoverage=true + /p:CoverletOutputFormat="opencover" +- codecov -f "%CD%\Tests\%SOLUTION_NAME%.Tests\coverage.opencover.xml" +- java + -jar + ./codacy-test-reporter.jar report + -l CSharp + -t %CODACY_PROJECT_TOKEN% + -r "%CD%\Tests\%SOLUTION_NAME%.Tests\coverage.opencover.xml" +- ps: 'if (-Not $env:APPVEYOR_PULL_REQUEST_NUMBER -And $env:APPVEYOR_REPO_BRANCH -eq "master") { & + dotnet sonarscanner end + /d:sonar.login="$env:SONAR_TOKEN" }' + +after_build: +- xcopy %CD%\Src\%SOLUTION_NAME%\bin\Release\netstandard2.0\*.* %CD%\Build\ +- copy %CD%\Src\%SOLUTION_NAME%\bin\Release\%SOLUTION_NAME%.%APPVEYOR_BUILD_VERSION%.nupkg %SOLUTION_NAME%.%APPVEYOR_BUILD_VERSION%.nupkg +- rd /s /q %CD%\Src\%SOLUTION_NAME%\bin\Release\ +- xcopy %CD%\Tests\%SOLUTION_NAME%.Tests\*.xml %CD%\Coverage\ +- xcopy %CD%\Tests\%SOLUTION_NAME%.Tests\*.json %CD%\Coverage\ +- cd %CD% +- 7z a -tzip -mx9 "%SOLUTION_NAME%.%APPVEYOR_BUILD_VERSION%.zip" Build +- 7z a -tzip -mx9 "%SOLUTION_NAME%.%APPVEYOR_BUILD_VERSION%.Coverage.zip" Coverage + +artifacts: +- path: $(SOLUTION_NAME).%APPVEYOR_BUILD_VERSION%.zip + name: ZipFile +- path: $(SOLUTION_NAME).%APPVEYOR_BUILD_VERSION%.nupkg + name: Package +- path: $(SOLUTION_NAME).%APPVEYOR_BUILD_VERSION%.Coverage.zip + name: Coverage + +deploy: +- provider: Environment + name: NuGet + on: + branch: master + artifact: Package +- provider: GitHub + on: + branch: master + tag: $(appveyor_build_version) + auth_token: $(GITHUB_TOKEN) + force_update: true + artifact: ZipFile From 8cdf12077c86727559b7bf356bed9001ed39aaf2 Mon Sep 17 00:00:00 2001 From: Guilherme Branco Stracini Date: Fri, 24 Jul 2020 09:08:33 -0300 Subject: [PATCH 3/3] Sonarcube --- .../Composition/ServiceLocator.cs | 180 ++-- Src/CrispyWaffle/Cryptography/Security.cs | 227 +++-- .../Extensions/ConversionExtensions.cs | 895 ++++++++-------- .../Extensions/StringExtensions.cs | 960 +++++++++--------- Src/CrispyWaffle/Extensions/TypeExtensions.cs | 336 +++--- .../Log/Handlers/DefaultExceptionHandler.cs | 47 +- Src/CrispyWaffle/Log/LogConsumer.cs | 62 +- .../Adapters/BinarySerializerAdapter.cs | 322 +++--- .../Serialization/SerializerFactory.cs | 407 ++++---- .../Validations/PersonalDataValidation.cs | 61 +- 10 files changed, 1860 insertions(+), 1637 deletions(-) diff --git a/Src/CrispyWaffle/Composition/ServiceLocator.cs b/Src/CrispyWaffle/Composition/ServiceLocator.cs index 794df7b6..4215ee3b 100644 --- a/Src/CrispyWaffle/Composition/ServiceLocator.cs +++ b/Src/CrispyWaffle/Composition/ServiceLocator.cs @@ -119,53 +119,70 @@ private static void RegisterLifeStyledInternal(LifeStyle lifeStyle, Type contrac { RegistrationsCalls.Add(contract, 0); - #region Transient - if (lifeStyle == LifeStyle.TRANSIENT) { - Registrations.AddOrUpdate(contract, - () => - { - RegistrationsCalls[contract]++; - return GetInstance(implementation); - }, - (key, existingValue) => () => existingValue); + RegisterTransientInternal(contract, implementation); return; } - #endregion - - #region Singleton - IDisposable - if (implementation.Implements()) { - var lazyDisposable = new LazyDisposable(() => (IDisposable)CreateInstance(implementation)); - Registrations.AddOrUpdate(contract, - () => - { - RegistrationsCalls[contract]++; - lock (Locks.GetOrAdd(implementation.FullName ?? implementation.Name, new object())) - return lazyDisposable.Value; - }, - (key, existingValue) => () => existingValue); + RegisterDisposableInternal(contract, implementation); return; } - #endregion - - #region Singleton + RegisterSingletonInternal(contract, implementation); + } + /// + /// Registers the singleton internal. + /// + /// The contract. + /// The implementation. + private static void RegisterSingletonInternal(Type contract, Type implementation) + { var lazy = new Lazy(() => CreateInstance(implementation)); Registrations.AddOrUpdate(contract, - () => - { - RegistrationsCalls[contract]++; - lock (Locks.GetOrAdd(implementation.FullName ?? implementation.Name, new object())) - return lazy.Value; - }, - (key, existingValue) => () => existingValue); - #endregion + () => + { + RegistrationsCalls[contract]++; + lock (Locks.GetOrAdd(implementation.FullName ?? implementation.Name, new object())) + return lazy.Value; + }, + (key, existingValue) => () => existingValue); + } + /// + /// Registers the disposable internal. + /// + /// The contract. + /// The implementation. + private static void RegisterDisposableInternal(Type contract, Type implementation) + { + var lazyDisposable = new LazyDisposable(() => (IDisposable) CreateInstance(implementation)); + Registrations.AddOrUpdate(contract, + () => + { + RegistrationsCalls[contract]++; + lock (Locks.GetOrAdd(implementation.FullName ?? implementation.Name, new object())) + return lazyDisposable.Value; + }, + (key, existingValue) => () => existingValue); + } + /// + /// Registers the transient internal. + /// + /// The contract. + /// The implementation. + private static void RegisterTransientInternal(Type contract, Type implementation) + { + Registrations.AddOrUpdate(contract, + () => + { + RegistrationsCalls[contract]++; + return GetInstance(implementation); + }, + (key, existingValue) => () => existingValue); } /// @@ -181,57 +198,77 @@ private static void RegisterLifeStyledCreatorInternal( var contract = typeof(TContract); RegistrationsCalls.Add(contract, 0); - #region Transient - if (lifeStyle == LifeStyle.TRANSIENT) { - Registrations.AddOrUpdate( - contract, - () => - { - RegistrationsCalls[contract]++; - return instanceCreator(); - }, - (key, existingValue) => () => existingValue); + RegisterTransientInternal(instanceCreator, contract); return; } - #endregion - - #region Singleton - IDisposable - if (contract.Implements()) { - var lazyDisposable = new LazyDisposable(() => (IDisposable)instanceCreator()); - Registrations.AddOrUpdate( - contract, - () => - { - RegistrationsCalls[contract]++; - lock (Locks.GetOrAdd(contract.FullName ?? contract.Name, new object())) - return lazyDisposable.Value; - }, - (key, existingValue) => () => existingValue); + RegisterDisposableInternal(instanceCreator, contract); return; } - #endregion - - #region Singleton + RegisterSingleton(instanceCreator, contract); + } + /// + /// Registers the singleton. + /// + /// The type of the t contract. + /// The instance creator. + /// The contract. + private static void RegisterSingleton(Func instanceCreator, Type contract) + { var lazy = new Lazy(() => instanceCreator()); Registrations.AddOrUpdate( - contract, - () => - { - RegistrationsCalls[contract]++; - lock (Locks.GetOrAdd(contract.FullName ?? contract.Name, new object())) - return lazy.Value; - }, - (key, existingValue) => () => existingValue); + contract, + () => + { + RegistrationsCalls[contract]++; + lock (Locks.GetOrAdd(contract.FullName ?? contract.Name, new object())) + return lazy.Value; + }, + (key, existingValue) => () => existingValue); + } + /// + /// Registers the disposable internal. + /// + /// The type of the t contract. + /// The instance creator. + /// The contract. + private static void RegisterDisposableInternal(Func instanceCreator, Type contract) + { + var lazyDisposable = new LazyDisposable(() => (IDisposable) instanceCreator()); + Registrations.AddOrUpdate( + contract, + () => + { + RegistrationsCalls[contract]++; + lock (Locks.GetOrAdd(contract.FullName ?? contract.Name, new object())) + return lazyDisposable.Value; + }, + (key, existingValue) => () => existingValue); + } - #endregion + /// + /// Registers the transient internal. + /// + /// The type of the t contract. + /// The instance creator. + /// The contract. + private static void RegisterTransientInternal(Func instanceCreator, Type contract) + { + Registrations.AddOrUpdate( + contract, + () => + { + RegistrationsCalls[contract]++; + return instanceCreator(); + }, + (key, existingValue) => () => existingValue); } /// @@ -314,14 +351,19 @@ private static object CreateInstanceInternal(Type implementationType) .Select((type, i) => GetInstanceWithContext(type, implementationType, i)) .ToArray()); + var constructors = implementationType.GetConstructors(); + var ctor = constructors.Length == 1 ? constructors.Single() : ResolveMultipleConstructors(constructors, implementationType); + if (ctor == null) return null; + dependencies = ctor.GetParameters().Select(p => p.ParameterType).ToArray(); DependenciesCache.Add(implementationType, dependencies); + return Activator.CreateInstance(implementationType, dependencies .Select((type, i) => diff --git a/Src/CrispyWaffle/Cryptography/Security.cs b/Src/CrispyWaffle/Cryptography/Security.cs index eb304b23..e69439fe 100644 --- a/Src/CrispyWaffle/Cryptography/Security.cs +++ b/Src/CrispyWaffle/Cryptography/Security.cs @@ -1,112 +1,115 @@ -namespace CrispyWaffle.Cryptography -{ - using System; - using System.ComponentModel; - using System.Globalization; - using System.IO; - using System.Security.Cryptography; - using System.Text; - - /// - /// Class Security. - /// - public static class Security - { - /// - /// Encrypts the specified plain text. - /// - /// The plain text. - /// The password hash - /// The salt key - /// The vi key - /// String. - public static string Encrypt(this string plainText, string passwordHash, string saltKey, string viKey) - { - var plainTextBytes = Encoding.UTF8.GetBytes(plainText); - - var keyBytes = new Rfc2898DeriveBytes(passwordHash, Encoding.ASCII.GetBytes(saltKey)).GetBytes(256 / 8); - var symmetricKey = new RijndaelManaged { Mode = CipherMode.CBC, Padding = PaddingMode.Zeros }; - var encryption = symmetricKey.CreateEncryptor(keyBytes, Encoding.ASCII.GetBytes(viKey)); - - byte[] cipherTextBytes; - - var memoryStream = new MemoryStream(); - using (var cryptoStream = new CryptoStream(memoryStream, encryption, CryptoStreamMode.Write)) - { - cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length); - cryptoStream.FlushFinalBlock(); - cipherTextBytes = memoryStream.ToArray(); - } - - return Convert.ToBase64String(cipherTextBytes); - } - - /// - /// Decrypts the specified encrypted text. - /// - /// The encrypted text. - /// The password hash - /// The salt key - /// The vi key - /// String. - [Localizable(false)] - public static string Decrypt(this string encryptedText, string passwordHash, string saltKey, string viKey) - { - var cipherTextBytes = Convert.FromBase64String(encryptedText); - var keyBytes = new Rfc2898DeriveBytes(passwordHash, Encoding.ASCII.GetBytes(saltKey)).GetBytes(256 / 8); - var symmetricKey = new RijndaelManaged { Mode = CipherMode.CBC, Padding = PaddingMode.None }; - - var decryption = symmetricKey.CreateDecryptor(keyBytes, Encoding.ASCII.GetBytes(viKey)); - var memoryStream = new MemoryStream(cipherTextBytes); - - byte[] plainTextBytes; - int decryptedByteCount; - - using (var cryptoStream = new CryptoStream(memoryStream, decryption, CryptoStreamMode.Read)) - { - plainTextBytes = new byte[cipherTextBytes.Length]; - decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length); - } - - return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount).TrimEnd("\0".ToCharArray()); - } - - /// - /// Generates a hash for the requested value using the desired hash algorithm - /// - /// The value to compute the hash - /// The hash algorithm - /// The hash of the value - public static string Hash(string value, HashAlgorithmType type) - { - HashAlgorithm algorithm; - switch (type) - { - case HashAlgorithmType.MD5: - algorithm = new MD5CryptoServiceProvider(); - break; - case HashAlgorithmType.SHA1: - algorithm = SHA1.Create(); - break; - case HashAlgorithmType.SHA256: - algorithm = SHA256.Create(); - break; - case HashAlgorithmType.SHA384: - algorithm = SHA384.Create(); - break; - case HashAlgorithmType.SHA512: - algorithm = SHA512.Create(); - break; - default: - throw new ArgumentOutOfRangeException(nameof(type), type, null); - } - var hash = algorithm.ComputeHash(Encoding.UTF8.GetBytes(value)); - var result = new StringBuilder(); - foreach (var t in hash) - result.Append(type == HashAlgorithmType.MD5 - ? t.ToString(@"x2") - : t.ToString(CultureInfo.InvariantCulture)); - return result.ToString(); - } - } -} +using System.Collections.Generic; + +namespace CrispyWaffle.Cryptography +{ + using System; + using System.ComponentModel; + using System.Globalization; + using System.IO; + using System.Security.Cryptography; + using System.Text; + + /// + /// Class Security. + /// + public static class Security + { + /// + /// Encrypts the specified plain text. + /// + /// The plain text. + /// The password hash + /// The salt key + /// The vi key + /// String. + public static string Encrypt(this string plainText, string passwordHash, string saltKey, string viKey) + { + var plainTextBytes = Encoding.UTF8.GetBytes(plainText); + + var keyBytes = new Rfc2898DeriveBytes(passwordHash, Encoding.ASCII.GetBytes(saltKey)).GetBytes(256 / 8); + var symmetricKey = new RijndaelManaged { Mode = CipherMode.CBC, Padding = PaddingMode.Zeros }; + var encryption = symmetricKey.CreateEncryptor(keyBytes, Encoding.ASCII.GetBytes(viKey)); + + byte[] cipherTextBytes; + + var memoryStream = new MemoryStream(); + + using (var cryptoStream = new CryptoStream(memoryStream, encryption, CryptoStreamMode.Write)) + { + cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length); + cryptoStream.FlushFinalBlock(); + cipherTextBytes = memoryStream.ToArray(); + } + + return Convert.ToBase64String(cipherTextBytes); + } + + /// + /// Decrypts the specified encrypted text. + /// + /// The encrypted text. + /// The password hash + /// The salt key + /// The vi key + /// String. + [Localizable(false)] + public static string Decrypt(this string encryptedText, string passwordHash, string saltKey, string viKey) + { + var cipherTextBytes = Convert.FromBase64String(encryptedText); + var keyBytes = new Rfc2898DeriveBytes(passwordHash, Encoding.ASCII.GetBytes(saltKey)).GetBytes(256 / 8); + var symmetricKey = new RijndaelManaged { Mode = CipherMode.CBC, Padding = PaddingMode.None }; + + var decryption = symmetricKey.CreateDecryptor(keyBytes, Encoding.ASCII.GetBytes(viKey)); + var memoryStream = new MemoryStream(cipherTextBytes); + + byte[] plainTextBytes; + int decryptedByteCount; + + using (var cryptoStream = new CryptoStream(memoryStream, decryption, CryptoStreamMode.Read)) + { + plainTextBytes = new byte[cipherTextBytes.Length]; + decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length); + } + + return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount).TrimEnd("\0".ToCharArray()); + } + + /// + /// The hash algorithms + /// + private static Dictionary hashAlgorithms = new Dictionary + { + {HashAlgorithmType.MD5, new MD5CryptoServiceProvider()}, + {HashAlgorithmType.SHA1, SHA1.Create()}, + {HashAlgorithmType.SHA256, SHA256.Create()}, + {HashAlgorithmType.SHA384, SHA384.Create()}, + {HashAlgorithmType.SHA512, SHA512.Create()} + }; + + /// + /// Generates a hash for the requested value using the desired hash algorithm + /// + /// The value to compute the hash + /// The hash algorithm + /// The hash of the value + public static string Hash(string value, HashAlgorithmType type) + { + + if (!hashAlgorithms.ContainsKey(type)) + throw new ArgumentOutOfRangeException(nameof(type), type, "Invalid algorithm type"); + + var algorithm = hashAlgorithms[type]; + + var hash = algorithm.ComputeHash(Encoding.UTF8.GetBytes(value)); + + var result = new StringBuilder(); + + foreach (var t in hash) + result.Append(type == HashAlgorithmType.MD5 + ? t.ToString(@"x2") + : t.ToString(CultureInfo.InvariantCulture)); + + return result.ToString(); + } + } +} diff --git a/Src/CrispyWaffle/Extensions/ConversionExtensions.cs b/Src/CrispyWaffle/Extensions/ConversionExtensions.cs index 17a606e3..425c2a58 100644 --- a/Src/CrispyWaffle/Extensions/ConversionExtensions.cs +++ b/Src/CrispyWaffle/Extensions/ConversionExtensions.cs @@ -1,442 +1,453 @@ -namespace CrispyWaffle.Extensions -{ - using GoodPractices; - using Newtonsoft.Json.Linq; - using System; - using System.Collections.Generic; - using System.Globalization; - using System.Linq; - using System.Reflection; - using System.Text; - using System.Text.RegularExpressions; - using System.Xml; - using Utilities; - - /// - /// Helper class for generic conversions - /// - public static class ConversionExtensions - { - /// - /// An IEnumerable<String> extension method that converts a binary representation string to the bytes. - /// - /// The string to act on. - /// string in binary representation as a Byte[]. - public static byte[] ToBytes(this IEnumerable input) - { - return input.Select(s => Convert.ToByte(s, 2)).ToArray(); - } - - /// - /// A String extension method that converts this object to a boolean. This method assumes - /// that any value other than stated in the optional parameter toTrue will be valid as - /// false. It is useful for converting binary flags that can only take on two distinct - /// values​​, or that only one value represents success and any other is invalid. - /// - /// The str to act on. - /// (Optional) the valid value for true. - /// The given data converted to a Boolean. - public static bool ToBoolean(this string str, string validValueForTrue = "S") - { - return str != null && str.Equals(validValueForTrue, StringExtensions.Comparison); - } - - /// - /// A Boolean extension method that convert this object into a String representation. - /// - /// The boolean to act on. - /// The value if true. - /// The value if false. - /// The given data converted to a String. - public static string ToString(this bool boolean, string trueValue, string falseValue) - { - return boolean ? trueValue : falseValue; - } - - /// - /// To the date time. - /// - /// The input. - /// - /// input - /// input - public static DateTime ToDateTime(this string input) - { - if (string.IsNullOrWhiteSpace(input)) - throw new ArgumentNullException(nameof(input), "Input value cannot be null"); - input = input.ToLower().Trim(); - switch (input) - { - case "agora": - case "now": - return DateTime.Now; - case "hoje": - case "today": - return DateTime.Today; - case "ontem": - case "yesterday": - return DateTime.Today.AddDays(-1); - case "amanhã": - case "amanha": - case "tomorrow": - return DateTime.Today.AddDays(1); - default: - if (DateTime.TryParse(input, out var result)) - return result; - if (input.Length == 10 && - DateTime.TryParseExact(input, - @"dd/MM/yyyy", - CultureInfo.InvariantCulture, - DateTimeStyles.None, - out result)) - return result; - throw new ArgumentOutOfRangeException(nameof(input), input, "Unable to parse the string to a valid datetime"); - } - } - - /// - /// Tries to convert string to date time. - /// - /// The input string a valid DateTime format. - /// The DateTime value. - /// True if success, false otherwise - public static bool TryToDateTime(this string input, out DateTime value) - { - value = DateTime.MinValue; - if (string.IsNullOrWhiteSpace(input)) - return false; - input = input.ToLower().Trim(); - switch (input) - { - case "agora": - case "now": - value = DateTime.Now; - return true; - case "hoje": - case "today": - value = DateTime.Today; - return true; - case "ontem": - case "yesterday": - value = DateTime.Today.AddDays(-1); - return true; - case "amanhã": - case "amanha": - case "tomorrow": - value = DateTime.Today.AddDays(1); - return true; - default: - if (DateTime.TryParse(input, out value)) - return true; - return input.Length == 10 && - DateTime.TryParseExact(input, - @"dd/MM/yyyy", - CultureInfo.InvariantCulture, - DateTimeStyles.None, - out value); - } - } - - /// - /// Convert a string representation of Int32 to Int32 type - /// - /// The string representation of a Int32 - /// The string as Int32 - public static int ToInt32(this string input) - { - if (string.IsNullOrWhiteSpace(input)) - return 0; - var success = int.TryParse(input, NumberStyles.Number, StringExtensions.Culture, out var result); - return success ? result : 0; - } - - /// - /// To the int64. - /// - /// The input. - /// Int64. - public static long ToInt64(this string input) - { - if (string.IsNullOrWhiteSpace(input)) - return 0; - var success = long.TryParse(input, NumberStyles.Number, StringExtensions.Culture, out var result); - return success ? result : 0; - } - - /// - /// A String extension method that converts a string to a decimal. - /// - /// The str to act on. - /// str as a Decimal. - public static decimal ToDecimal(this string input) - { - if (string.IsNullOrWhiteSpace(input)) - return 0M; - return decimal.TryParse(input, NumberStyles.Number, StringExtensions.Culture, out var result) - ? result - : 0M; - } - - /// - /// An IEnumerable<Byte> extension method - /// that converts the bytes to a binary string representation - /// string. - /// - /// The bytes to act on. - /// bytes as a Binary String[] representation. - public static string[] ToBinaryString(this IEnumerable bytes) - { - return bytes.Select(b => Convert.ToString(b, 2).PadLeft(8, '0')).ToArray(); - } - - /// - /// Converts a decimal input to monetary readable format (PT-BR - BRL) - /// - /// - /// - public static string ToMonetary(this decimal input) - { - return input == 0 ? "No value" : $@"R${input:### ##0.00}"; - } - - /// - /// Converts a DateTime instance to Unix Timestamp (number of seconds that have elapsed since - /// 00:00:00 Coordinated Universal Time (UTC), Thursday, 1 January 1970) - /// - /// - /// - public static int ToUnixTimeStamp(this DateTime dateTime) - { - return (int)dateTime.ToUniversalTime().Subtract(new DateTime(1970, 1, 1)).TotalSeconds; - } - - /// - /// Converts a Unix Timestamp (number of seconds that have elapsed since 00:00:00 Coordinated - /// Universal Time (UTC), Thursday, 1 January 1970) to a DateTime instance - /// - /// The Unix Timestamp - /// A DateTime instance of the epochTime - public static DateTime FromUnixTimeStamp(this int epochTime) - { - return new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) - .AddSeconds(Math.Round((double)epochTime / 1000)) - .ToLocalTime(); - } - - /// - /// Parses the brazilian phone number. - /// - /// The number. - /// - /// - public static PhoneNumber ParseBrazilianPhoneNumber(this string number) - { - var result = new PhoneNumber(0, 0, 0); - if (number.TryParseBrazilianPhoneNumber(ref result)) - return result; - throw new InvalidTelephoneNumberException(number.RemoveNonNumeric()); - } - - /// - /// Tries the parse brazilian phone number. - /// - /// The number. - /// The result. - /// - public static bool TryParseBrazilianPhoneNumber(this string number, ref PhoneNumber result) - { - var dirty = number.RemoveNonNumeric(); - var dirtyLength = dirty.Length; - - if (dirty.StartsWith(@"55") && dirtyLength > 11 && dirtyLength < 15) - { - dirty = dirty.Remove(0, 2); - dirtyLength -= 2; - } - - if (dirtyLength < 10 || - dirtyLength > 12) - return false; - - var prefix = dirty.Substring(0, 1).Equals(@"0", StringExtensions.Comparison) && - (dirtyLength == 11 || dirtyLength == 12) - ? dirty.Substring(1, 2) - : dirty.Substring(0, 2); - var hasNineDigits = dirty.Substring(dirtyLength - 9, 1) - .Equals(@"9", StringExtensions.Comparison); - var allowedDigits = hasNineDigits ? 9 : 8; - var telephoneNumber = dirty.Substring(dirtyLength - allowedDigits, allowedDigits); - result = new PhoneNumber(55, prefix.ToInt32(), telephoneNumber.ToInt64()); - return true; - } - - /// - /// To the pretty string. - /// - /// The json. - /// - public static string ToPrettyString(this string json) - { - if (string.IsNullOrWhiteSpace(json)) - return string.Empty; - - if (!json.IsValidJson()) - return json; - - var parsedJson = JToken.Parse(json); - - return parsedJson.ToString(Newtonsoft.Json.Formatting.Indented); - } - - /// - /// To the ident string. - /// - /// The document. - /// System.String. - public static string ToIdentString(this XmlDocument document) - { - if (document == null) - return null; - var builder = new StringBuilder(); - - var settings = new XmlWriterSettings - { - Indent = true, - IndentChars = "\t", - NewLineChars = "\r\n", - NewLineHandling = NewLineHandling.Replace - }; - - using (var writer = XmlWriter.Create(builder, settings)) - document.Save(writer); - - return builder.ToString(); - } - - /// - /// Calculates the module-10 of a string. - /// - /// The input. - /// System.Int32. - public static int ToModule10(this string input) - { - var number = Regex.Replace(input, "[^0-9]", ""); - var sum = 0; - var weight = 2; - var counter = number.Length - 1; - while (counter >= 0) - { - var multiplication = number.Substring(counter, 1).ToInt32() * weight; - if (multiplication >= 10) - multiplication = 1 + (multiplication - 10); - sum += multiplication; - weight = weight == 2 ? 1 : 2; - counter--; - } - var digit = 10 - sum % 10; - if (digit == 10) - digit = 0; - return digit; - } - - /// - /// To the ordinal. - /// - /// The number. - /// - public static string ToOrdinal(this long number) - { - if (number < 0) - return number.ToString(); - var rem = number % 100; - if (rem >= 11 && rem <= 13) - return $"{number}th"; - switch (number % 10) - { - case 1: - return $"{number}st"; - case 2: - return $"{number}nd"; - case 3: - return $"{number}rd"; - default: - return $"{number}th"; - } - } - - /// - /// To the ordinal. - /// - /// The number. - /// - public static string ToOrdinal(this int number) - { - return ((long)number).ToOrdinal(); - } - - /// - /// Formats the document. - /// - /// The document. - /// - public static string FormatBrazilianDocument(this string document) - { - if (string.IsNullOrWhiteSpace(document)) - return "Invalid document"; - var documentPattern = document.Length == 14 ? @"{0:00\.000\.000/0000-00}" : @"{0:000\.000\.000-00}"; - return string.Format(documentPattern, document.RemoveNonNumeric().ToInt64()); - } - - /// - /// Formats the zip code. - /// - /// The zip code. - /// - public static string FormatBrazilianZipCode(this string zipCode) - { - return Regex.Replace(zipCode.RemoveNonNumeric(), @"(\d{5})(\d{3})", "$1-$2"); - } - - /// - /// Deeps the clone. - /// - /// - /// The instance. - /// if set to true [use non public]. - /// - public static T DeepClone(this T instance, bool useNonPublic = true) - { - var type = typeof(T); - - var constructors = type.GetConstructors().OrderByDescending(c => c.GetParameters().Length); - var ctor = constructors.FirstOrDefault(); - - if (ctor == null) - return default; - - var arguments = new List(); - var parameters = ctor.GetParameters(); - foreach (var parameter in parameters) - { - var property = type.GetProperty(parameter.Name, BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance); - - if (property == null && !useNonPublic) - continue; - - if (property == null) - { - property = type.GetProperty(parameter.Name, BindingFlags.NonPublic | BindingFlags.IgnoreCase | BindingFlags.Instance); - - if (property == null) - continue; - } - - if (property.PropertyType != parameter.ParameterType) - continue; - - arguments.Add(property.GetValue(instance)); - } - return (T)ctor.Invoke(arguments.ToArray()); - } - } -} +namespace CrispyWaffle.Extensions +{ + using GoodPractices; + using Newtonsoft.Json.Linq; + using System; + using System.Collections.Generic; + using System.Globalization; + using System.Linq; + using System.Reflection; + using System.Text; + using System.Text.RegularExpressions; + using System.Xml; + using Utilities; + + /// + /// Helper class for generic conversions + /// + public static class ConversionExtensions + { + /// + /// An IEnumerable<String> extension method that converts a binary representation string to the bytes. + /// + /// The string to act on. + /// string in binary representation as a Byte[]. + public static byte[] ToBytes(this IEnumerable input) + { + return input.Select(s => Convert.ToByte(s, 2)).ToArray(); + } + + /// + /// A String extension method that converts this object to a boolean. This method assumes + /// that any value other than stated in the optional parameter toTrue will be valid as + /// false. It is useful for converting binary flags that can only take on two distinct + /// values​​, or that only one value represents success and any other is invalid. + /// + /// The str to act on. + /// (Optional) the valid value for true. + /// The given data converted to a Boolean. + public static bool ToBoolean(this string str, string validValueForTrue = "S") + { + return str != null && str.Equals(validValueForTrue, StringExtensions.Comparison); + } + + /// + /// A Boolean extension method that convert this object into a String representation. + /// + /// The boolean to act on. + /// The value if true. + /// The value if false. + /// The given data converted to a String. + public static string ToString(this bool boolean, string trueValue, string falseValue) + { + return boolean ? trueValue : falseValue; + } + + /// + /// To the date time. + /// + /// The input. + /// + /// input + /// input + public static DateTime ToDateTime(this string input) + { + if (string.IsNullOrWhiteSpace(input)) + throw new ArgumentNullException(nameof(input), "Input value cannot be null"); + input = input.ToLower().Trim(); + switch (input) + { + case "agora": + case "now": + return DateTime.Now; + case "hoje": + case "today": + return DateTime.Today; + case "ontem": + case "yesterday": + return DateTime.Today.AddDays(-1); + case "amanhã": + case "amanha": + case "tomorrow": + return DateTime.Today.AddDays(1); + default: + if (DateTime.TryParse(input, out var result)) + return result; + if (input.Length == 10 && + DateTime.TryParseExact(input, + @"dd/MM/yyyy", + CultureInfo.InvariantCulture, + DateTimeStyles.None, + out result)) + return result; + throw new ArgumentOutOfRangeException(nameof(input), input, "Unable to parse the string to a valid datetime"); + } + } + + /// + /// Tries to convert string to date time. + /// + /// The input string a valid DateTime format. + /// The DateTime value. + /// True if success, false otherwise + public static bool TryToDateTime(this string input, out DateTime value) + { + value = DateTime.MinValue; + if (string.IsNullOrWhiteSpace(input)) + return false; + input = input.ToLower().Trim(); + switch (input) + { + case "agora": + case "now": + value = DateTime.Now; + return true; + case "hoje": + case "today": + value = DateTime.Today; + return true; + case "ontem": + case "yesterday": + value = DateTime.Today.AddDays(-1); + return true; + case "amanhã": + case "amanha": + case "tomorrow": + value = DateTime.Today.AddDays(1); + return true; + default: + if (DateTime.TryParse(input, out value)) + return true; + return input.Length == 10 && + DateTime.TryParseExact(input, + @"dd/MM/yyyy", + CultureInfo.InvariantCulture, + DateTimeStyles.None, + out value); + } + } + + /// + /// Convert a string representation of Int32 to Int32 type + /// + /// The string representation of a Int32 + /// The string as Int32 + public static int ToInt32(this string input) + { + if (string.IsNullOrWhiteSpace(input)) + return 0; + var success = int.TryParse(input, NumberStyles.Number, StringExtensions.Culture, out var result); + return success ? result : 0; + } + + /// + /// To the int64. + /// + /// The input. + /// Int64. + public static long ToInt64(this string input) + { + if (string.IsNullOrWhiteSpace(input)) + return 0; + var success = long.TryParse(input, NumberStyles.Number, StringExtensions.Culture, out var result); + return success ? result : 0; + } + + /// + /// A String extension method that converts a string to a decimal. + /// + /// The str to act on. + /// str as a Decimal. + public static decimal ToDecimal(this string input) + { + if (string.IsNullOrWhiteSpace(input)) + return 0M; + return decimal.TryParse(input, NumberStyles.Number, StringExtensions.Culture, out var result) + ? result + : 0M; + } + + /// + /// An IEnumerable<Byte> extension method + /// that converts the bytes to a binary string representation + /// string. + /// + /// The bytes to act on. + /// bytes as a Binary String[] representation. + public static string[] ToBinaryString(this IEnumerable bytes) + { + return bytes.Select(b => Convert.ToString(b, 2).PadLeft(8, '0')).ToArray(); + } + + /// + /// Converts a decimal input to monetary readable format (PT-BR - BRL) + /// + /// + /// + public static string ToMonetary(this decimal input) + { + return input == 0 ? "No value" : $@"R${input:### ##0.00}"; + } + + /// + /// Converts a DateTime instance to Unix Timestamp (number of seconds that have elapsed since + /// 00:00:00 Coordinated Universal Time (UTC), Thursday, 1 January 1970) + /// + /// + /// + public static int ToUnixTimeStamp(this DateTime dateTime) + { + return (int)dateTime.ToUniversalTime().Subtract(new DateTime(1970, 1, 1)).TotalSeconds; + } + + /// + /// Converts a Unix Timestamp (number of seconds that have elapsed since 00:00:00 Coordinated + /// Universal Time (UTC), Thursday, 1 January 1970) to a DateTime instance + /// + /// The Unix Timestamp + /// A DateTime instance of the epochTime + public static DateTime FromUnixTimeStamp(this int epochTime) + { + return new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + .AddSeconds(Math.Round((double)epochTime / 1000)) + .ToLocalTime(); + } + + /// + /// Parses the brazilian phone number. + /// + /// The number. + /// + /// + public static PhoneNumber ParseBrazilianPhoneNumber(this string number) + { + var result = new PhoneNumber(0, 0, 0); + if (number.TryParseBrazilianPhoneNumber(ref result)) + return result; + throw new InvalidTelephoneNumberException(number.RemoveNonNumeric()); + } + + /// + /// Tries the parse brazilian phone number. + /// + /// The number. + /// The result. + /// + public static bool TryParseBrazilianPhoneNumber(this string number, ref PhoneNumber result) + { + var dirty = number.RemoveNonNumeric(); + var dirtyLength = dirty.Length; + + if (dirty.StartsWith(@"55") && dirtyLength > 11 && dirtyLength < 15) + { + dirty = dirty.Remove(0, 2); + dirtyLength -= 2; + } + + if (dirtyLength < 10 || + dirtyLength > 12) + return false; + + var prefix = dirty.Substring(0, 1).Equals(@"0", StringExtensions.Comparison) && + (dirtyLength == 11 || dirtyLength == 12) + ? dirty.Substring(1, 2) + : dirty.Substring(0, 2); + var hasNineDigits = dirty.Substring(dirtyLength - 9, 1) + .Equals(@"9", StringExtensions.Comparison); + var allowedDigits = hasNineDigits ? 9 : 8; + var telephoneNumber = dirty.Substring(dirtyLength - allowedDigits, allowedDigits); + result = new PhoneNumber(55, prefix.ToInt32(), telephoneNumber.ToInt64()); + return true; + } + + /// + /// To the pretty string. + /// + /// The json. + /// + public static string ToPrettyString(this string json) + { + if (string.IsNullOrWhiteSpace(json)) + return string.Empty; + + if (!json.IsValidJson()) + return json; + + var parsedJson = JToken.Parse(json); + + return parsedJson.ToString(Newtonsoft.Json.Formatting.Indented); + } + + /// + /// To the ident string. + /// + /// The document. + /// System.String. + public static string ToIdentString(this XmlDocument document) + { + if (document == null) + return null; + var builder = new StringBuilder(); + + var settings = new XmlWriterSettings + { + Indent = true, + IndentChars = "\t", + NewLineChars = "\r\n", + NewLineHandling = NewLineHandling.Replace + }; + + using (var writer = XmlWriter.Create(builder, settings)) + document.Save(writer); + + return builder.ToString(); + } + + /// + /// Calculates the module-10 of a string. + /// + /// The input. + /// System.Int32. + public static int ToModule10(this string input) + { + var number = Regex.Replace(input, "[^0-9]", ""); + + var sum = 0; + + var weight = 2; + + var counter = number.Length - 1; + + while (counter >= 0) + { + var multiplication = number.Substring(counter, 1).ToInt32() * weight; + + if (multiplication >= 10) + multiplication = 1 + (multiplication - 10); + + sum += multiplication; + + weight = weight == 2 ? 1 : 2; + + counter--; + } + + var digit = 10 - sum % 10; + + if (digit == 10) + digit = 0; + + return digit; + } + + /// + /// To the ordinal. + /// + /// The number. + /// + public static string ToOrdinal(this long number) + { + if (number < 0) + return number.ToString(); + var rem = number % 100; + if (rem >= 11 && rem <= 13) + return $"{number}th"; + switch (number % 10) + { + case 1: + return $"{number}st"; + case 2: + return $"{number}nd"; + case 3: + return $"{number}rd"; + default: + return $"{number}th"; + } + } + + /// + /// To the ordinal. + /// + /// The number. + /// + public static string ToOrdinal(this int number) + { + return ((long)number).ToOrdinal(); + } + + /// + /// Formats the document. + /// + /// The document. + /// + public static string FormatBrazilianDocument(this string document) + { + if (string.IsNullOrWhiteSpace(document)) + return "Invalid document"; + var documentPattern = document.Length == 14 ? @"{0:00\.000\.000/0000-00}" : @"{0:000\.000\.000-00}"; + return string.Format(documentPattern, document.RemoveNonNumeric().ToInt64()); + } + + /// + /// Formats the zip code. + /// + /// The zip code. + /// + public static string FormatBrazilianZipCode(this string zipCode) + { + return Regex.Replace(zipCode.RemoveNonNumeric(), @"(\d{5})(\d{3})", "$1-$2"); + } + + /// + /// Deeps the clone. + /// + /// + /// The instance. + /// if set to true [use non public]. + /// + public static T DeepClone(this T instance, bool useNonPublic = true) + { + var type = typeof(T); + + var constructors = type.GetConstructors().OrderByDescending(c => c.GetParameters().Length); + var ctor = constructors.FirstOrDefault(); + + if (ctor == null) + return default; + + var arguments = new List(); + var parameters = ctor.GetParameters(); + foreach (var parameter in parameters) + { + var property = type.GetProperty(parameter.Name, BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance); + + if (property == null && !useNonPublic) + continue; + + if (property == null) + { + property = type.GetProperty(parameter.Name, BindingFlags.NonPublic | BindingFlags.IgnoreCase | BindingFlags.Instance); + + if (property == null) + continue; + } + + if (property.PropertyType != parameter.ParameterType) + continue; + + arguments.Add(property.GetValue(instance)); + } + return (T)ctor.Invoke(arguments.ToArray()); + } + } +} diff --git a/Src/CrispyWaffle/Extensions/StringExtensions.cs b/Src/CrispyWaffle/Extensions/StringExtensions.cs index 2302d106..655e0bd0 100644 --- a/Src/CrispyWaffle/Extensions/StringExtensions.cs +++ b/Src/CrispyWaffle/Extensions/StringExtensions.cs @@ -1,467 +1,493 @@ -namespace CrispyWaffle.Extensions -{ - using Newtonsoft.Json.Linq; - using System; - using System.Collections.Generic; - using System.Diagnostics.Contracts; - using System.Globalization; - using System.IO; - using System.Linq; - using System.Text; - using System.Text.RegularExpressions; - using Validations; - - /// - /// Class StringExtensions. - /// - public static class StringExtensions - { - /// - /// The culture - /// - public static readonly CultureInfo Culture = CultureInfo.GetCultureInfo("en-US"); - - /// - /// The comparison - /// - public static readonly StringComparison Comparison = StringComparison.InvariantCultureIgnoreCase; - - /// - /// Replaces the non alphanumeric. - /// - /// The input. - /// The replace with. - /// String. - [Pure] - public static string ReplaceNonAlphanumeric(this string input, string replaceWith) - { - return string.IsNullOrWhiteSpace(input) - ? input - : StringValidations.NonAlphanumericPattern.Replace(input, replaceWith); - } - - /// - /// Replaces the first. - /// - /// The input. - /// The search. - /// The replace. - /// String. - [Pure] - public static string ReplaceFirst(this string input, string search, string replace) - { - if (string.IsNullOrWhiteSpace(input)) - return input; - var pos = input.IndexOf(search, StringComparison.Ordinal); - return pos < 0 ? input : input.Substring(0, pos) + replace + input.Substring(pos + search.Length); - } - - /// - /// Removes the non numeric. - /// - /// The input. - /// - [Pure] - public static string RemoveNonNumeric(this string input) - { - return string.IsNullOrWhiteSpace(input) - ? input - : StringValidations.NonNumericPattern.Replace(input, string.Empty); - } - - /// - /// Removes the diacritics. - /// - /// The input. - /// String. - [Pure] - public static string RemoveDiacritics(this string input) - { - return string.IsNullOrWhiteSpace(input) - ? input - : Encoding.ASCII.GetString(Encoding.GetEncoding(1251).GetBytes(input)); - } - - /// - /// Removes the spaces. - /// - /// The input. - /// String. - [Pure] - public static string RemoveSpaces(this string input) - { - return string.IsNullOrWhiteSpace(input) - ? input - : StringValidations.SpacesPattern.Replace(input, string.Empty); - } - - /// - /// Removes the excess spaces. - /// - /// The input. - /// String. - [Pure] - public static string RemoveExcessSpaces(this string input) - { - return string.IsNullOrWhiteSpace(input) - ? input - : StringValidations.MultipleSpacesPattern.Replace(input, " "); - } - - /// - /// Removes the non alphanumeric. - /// - /// The input. - /// String. - - [Pure] - public static string RemoveNonAlphanumeric(this string input) - { - return string.IsNullOrWhiteSpace(input) - ? input - : StringValidations.NonAlphanumericPattern.Replace(input, string.Empty); - } - - /// - /// Abbreviates the specified maximum characters. - /// - /// The input. - /// The maximum characters. - /// if set to true [add ellipsis]. - /// - [Pure] - public static string Abbreviate(this string input, int maxCharacters, bool addEllipsis = true) - { - if (string.IsNullOrWhiteSpace(input)) - return string.Empty; - if (input.Length < maxCharacters) - return input; - return string.Concat(input.Substring(0, addEllipsis ? maxCharacters - 4 : maxCharacters), addEllipsis ? @"..." : string.Empty); - } - - /// - /// To the camel case. - /// - /// The input. - /// String. - - [Pure] - public static string ToCamelCase(this string input) - { - if (string.IsNullOrWhiteSpace(input)) - return string.Empty; - var words = input.Trim().Split(' '); - var sb = new StringBuilder(); - foreach (var w in words.Where(s => !string.IsNullOrWhiteSpace(s))) - { - var c = w.ToLower().ToCharArray(); - c[0] = char.ToUpper(c[0]); - sb.Append(c).Append(@" "); - } - return sb.ToString().Trim(); - } - - /// - /// To the uc words. - /// - /// The input. - /// String. - [Pure] - public static string ToUcWords(this string input) - { - return string.IsNullOrWhiteSpace(input) - ? string.Empty - : Regex.Replace(input.ToLower(), @"(?:^|\s|/|[0-9])[a-z]", m => m.Value.ToUpper()); - } - - /// - /// Upper case the words. - /// - /// The input. - /// To upper. - /// To lower. - /// - [Pure] - public static string UcWords(this string[] input, string[] toUpper, string[] toLower) - { - var result = new StringBuilder(); - if (!input.Any()) - return string.Empty; - foreach (var s in input) - { - result.Append(@" "); - if (toUpper.Contains(s.ToUpper())) - result.Append(s.ToUpper()); - else if (toLower.Contains(s.ToLower())) - result.Append(s.ToLower()); - else - result.Append(s.ToCamelCase()); - } - return result.ToString(); - } - - /// - /// A String extension method that takes a string safely for inclusion in a Uri. - /// - /// The input String to act on. - /// str as a String. - [Pure] - public static string ToSafeUrl(this string input) - { - if (string.IsNullOrWhiteSpace(input)) - return input; - var bytes = Encoding.GetEncoding("ISO-8859-8").GetBytes(input); - return Encoding.UTF8.GetString(bytes).Replace(@"-", string.Empty).Replace(@".", string.Empty).RemoveExcessSpaces().Trim().ReplaceNonAlphanumeric(@"-").Trim('-'); - - } - - - /// - /// Encodes a string to base64 using UTF-8 - /// - /// The input. - /// - [Pure] - public static string ToBase64(this string input) - { - if (string.IsNullOrWhiteSpace(input)) - return string.Empty; - var utf8Bytes = Encoding.UTF8.GetBytes(input); - return Convert.ToBase64String(utf8Bytes); - } - - /// - /// Decodes a base64 string using the specified encoding set - /// - /// The input. - /// The encoding. - /// - [Pure] - public static string FromBase64(this string input, string encoding = "ISO-8859-1") - { - if (string.IsNullOrWhiteSpace(input)) - return string.Empty; - var base64Bytes = Convert.FromBase64String(input); - return Encoding.GetEncoding(encoding).GetString(base64Bytes); - } - - /// - /// Converts a string from ISO-8859-2 encoding to UTF-8 encoding - /// - /// The input. - /// String. - [Pure] - public static string FromISO2UTF8(this string input) - { - if (string.IsNullOrWhiteSpace(input)) - return string.Empty; - - var iso = Encoding.GetEncoding("ISO-8859-1"); - var utf = Encoding.UTF8; - - var bytes = iso.GetBytes(input); - var utf8Bytes = Encoding.Convert(iso, utf, bytes); - return utf.GetString(utf8Bytes); - } - - /// - /// To the name of the valid file. - /// - /// Name of the file. - /// - [Pure] - public static string ToValidFileName(this string fileName) - { - return StringValidations.InvalidFileName.Replace(fileName, "_"); - } - - /// - /// A String extension method that calculates Levenshtein distance. - /// - /// The string to be compared. - /// The string to compare. - /// An Int32. - [Pure] - public static int Levenshtein(this string input, string inputToCompare) - { - var n = input.Length; - var m = inputToCompare.Length; - var d = new int[n + 1, m + 1]; - - if (n == 0) - return m; - - if (m == 0) - return n; - - for (var i = 0; i <= n; i++) - d[i, 0] = i; - - for (var j = 0; j <= m; j++) - d[0, j] = j; - - for (var i = 1; i <= n; i++) - { - for (var j = 1; j <= m; j++) - { - var cost = inputToCompare.Substring(j - 1, 1) == input.Substring(i - 1, 1) ? 0 : 1; - d[i, j] = Math.Min(Math.Min(d[i - 1, j] + 1, d[i, j - 1] + 1), d[i - 1, j - 1] + cost); - } - } - return d[n, m]; - } - - /// - /// A String extension method that calculates levenshtein distance with invariant culture and insensitive case. - /// - /// The string to be compared. - /// The string to compare. - /// An Int32. - [Pure] - public static int LevenshteinInvariantCulture(this string input, string inputToCompare) - { - input = input.RemoveDiacritics().ToLower(); - return input.Levenshtein(inputToCompare.RemoveDiacritics().ToLower()); - } - - /// - /// Gets the name of the path or file. - /// - /// The URL. - /// - [Pure] - public static string GetPathOrFileName(this string url) - { - var uri = new Uri(url); - return string.IsNullOrWhiteSpace(uri.GetFileExtension()) ? uri.AbsolutePath : uri.GetFileName(); - } - - /// - /// An URI extension method that gets file name. - /// - /// The URI to act on. - /// The file name. - [Pure] - public static string GetFileName(this Uri uri) - { - return string.IsNullOrWhiteSpace(uri.LocalPath) ? string.Empty : Path.GetFileName(uri.LocalPath); - } - - /// - /// An URI extension method that gets file extension. - /// - /// The URI to act on. - /// The file extension. - [Pure] - public static string GetFileExtension(this Uri uri) - { - return uri.LocalPath.GetFileExtension(); - } - - /// - /// Gets the file extension. - /// - /// Name of the file. - /// String. - - [Pure] - public static string GetFileExtension(this string fileName) - { - return string.IsNullOrWhiteSpace(fileName) - ? string.Empty - : Path.GetExtension(fileName); - } - - /// - /// To the center. - /// - /// The input. - /// The spacer. - /// Size of the line. - /// String. - [Pure] - public static string ToCenter(this string input, char spacer, int lineSize) - { - var half = lineSize - input.Length / 2; - return $@"{new string(spacer, half)}{input}{new string(spacer, input.Length % 2 == 1 ? ++half : half)}"; - } - - /// - /// Determines whether the string is a valid JSON object - /// - /// The raw json string. - /// Boolean. - [Pure] - public static bool IsValidJson(this string jsonRaw) - { - try - { - jsonRaw = jsonRaw.Trim(); - if ((!jsonRaw.StartsWith(@"{") || !jsonRaw.EndsWith(@"}")) - && (!jsonRaw.StartsWith(@"[") || !jsonRaw.EndsWith(@"]"))) - return false; - JToken.Parse(jsonRaw); - return true; - } - catch (Exception) - { - return false; - } - } - - /// - /// Split the string by chuck length, - /// - /// - /// - /// - [Pure] - public static IEnumerable SplitBy(this string str, int chunkLength) - { - if (string.IsNullOrEmpty(str)) - { - yield return string.Empty; - yield break; - } - if (chunkLength < 1) - throw new ArgumentException(@"Invalid chuck length", nameof(chunkLength)); - for (var i = 0; i < str.Length; i += chunkLength) - yield return str.Substring(i, chunkLength + i > str.Length ? str.Length - i : chunkLength); - } - - /// - /// Strips the tags. - /// - /// The source. - /// - [Pure] - public static string StripTags(this string source) - { - if (string.IsNullOrWhiteSpace(source)) - return string.Empty; - var array = new char[source.Length]; - var arrayIndex = 0; - var inside = false; - - foreach (var let in source) - { - switch (let) - { - case '<': - inside = true; - continue; - case '>': - inside = false; - continue; - default: - if (inside) - continue; - array[arrayIndex] = let; - arrayIndex++; - break; - } - } - return new string(array, 0, arrayIndex); - } - } -} +namespace CrispyWaffle.Extensions +{ + using Newtonsoft.Json.Linq; + using System; + using System.Collections.Generic; + using System.Diagnostics.Contracts; + using System.Globalization; + using System.IO; + using System.Linq; + using System.Text; + using System.Text.RegularExpressions; + using Validations; + + /// + /// Class StringExtensions. + /// + public static class StringExtensions + { + /// + /// The culture + /// + public static readonly CultureInfo Culture = CultureInfo.GetCultureInfo("en-US"); + + /// + /// The comparison + /// + public static readonly StringComparison Comparison = StringComparison.InvariantCultureIgnoreCase; + + /// + /// Replaces the non alphanumeric. + /// + /// The input. + /// The replace with. + /// String. + [Pure] + public static string ReplaceNonAlphanumeric(this string input, string replaceWith) + { + return string.IsNullOrWhiteSpace(input) + ? input + : StringValidations.NonAlphanumericPattern.Replace(input, replaceWith); + } + + /// + /// Replaces the first. + /// + /// The input. + /// The search. + /// The replace. + /// String. + [Pure] + public static string ReplaceFirst(this string input, string search, string replace) + { + if (string.IsNullOrWhiteSpace(input)) + return input; + var pos = input.IndexOf(search, StringComparison.Ordinal); + return pos < 0 ? input : input.Substring(0, pos) + replace + input.Substring(pos + search.Length); + } + + /// + /// Removes the non numeric. + /// + /// The input. + /// + [Pure] + public static string RemoveNonNumeric(this string input) + { + return string.IsNullOrWhiteSpace(input) + ? input + : StringValidations.NonNumericPattern.Replace(input, string.Empty); + } + + /// + /// Removes the diacritics. + /// + /// The input. + /// String. + [Pure] + public static string RemoveDiacritics(this string input) + { + return string.IsNullOrWhiteSpace(input) + ? input + : Encoding.ASCII.GetString(Encoding.GetEncoding(1251).GetBytes(input)); + } + + /// + /// Removes the spaces. + /// + /// The input. + /// String. + [Pure] + public static string RemoveSpaces(this string input) + { + return string.IsNullOrWhiteSpace(input) + ? input + : StringValidations.SpacesPattern.Replace(input, string.Empty); + } + + /// + /// Removes the excess spaces. + /// + /// The input. + /// String. + [Pure] + public static string RemoveExcessSpaces(this string input) + { + return string.IsNullOrWhiteSpace(input) + ? input + : StringValidations.MultipleSpacesPattern.Replace(input, " "); + } + + /// + /// Removes the non alphanumeric. + /// + /// The input. + /// String. + + [Pure] + public static string RemoveNonAlphanumeric(this string input) + { + return string.IsNullOrWhiteSpace(input) + ? input + : StringValidations.NonAlphanumericPattern.Replace(input, string.Empty); + } + + /// + /// Abbreviates the specified maximum characters. + /// + /// The input. + /// The maximum characters. + /// if set to true [add ellipsis]. + /// + [Pure] + public static string Abbreviate(this string input, int maxCharacters, bool addEllipsis = true) + { + if (string.IsNullOrWhiteSpace(input)) + return string.Empty; + if (input.Length < maxCharacters) + return input; + return string.Concat(input.Substring(0, addEllipsis ? maxCharacters - 4 : maxCharacters), addEllipsis ? @"..." : string.Empty); + } + + /// + /// To the camel case. + /// + /// The input. + /// String. + + [Pure] + public static string ToCamelCase(this string input) + { + if (string.IsNullOrWhiteSpace(input)) + return string.Empty; + var words = input.Trim().Split(' '); + var sb = new StringBuilder(); + foreach (var w in words.Where(s => !string.IsNullOrWhiteSpace(s))) + { + var c = w.ToLower().ToCharArray(); + c[0] = char.ToUpper(c[0]); + sb.Append(c).Append(@" "); + } + return sb.ToString().Trim(); + } + + /// + /// To the uc words. + /// + /// The input. + /// String. + [Pure] + public static string ToUcWords(this string input) + { + return string.IsNullOrWhiteSpace(input) + ? string.Empty + : Regex.Replace(input.ToLower(), @"(?:^|\s|/|[0-9])[a-z]", m => m.Value.ToUpper()); + } + + /// + /// Upper case the words. + /// + /// The input. + /// To upper. + /// To lower. + /// + [Pure] + public static string UcWords(this string[] input, string[] toUpper, string[] toLower) + { + var result = new StringBuilder(); + if (!input.Any()) + return string.Empty; + foreach (var s in input) + { + result.Append(@" "); + if (toUpper.Contains(s.ToUpper())) + result.Append(s.ToUpper()); + else if (toLower.Contains(s.ToLower())) + result.Append(s.ToLower()); + else + result.Append(s.ToCamelCase()); + } + return result.ToString(); + } + + /// + /// A String extension method that takes a string safely for inclusion in a Uri. + /// + /// The input String to act on. + /// str as a String. + [Pure] + public static string ToSafeUrl(this string input) + { + if (string.IsNullOrWhiteSpace(input)) + return input; + var bytes = Encoding.GetEncoding("ISO-8859-8").GetBytes(input); + return Encoding.UTF8.GetString(bytes).Replace(@"-", string.Empty).Replace(@".", string.Empty).RemoveExcessSpaces().Trim().ReplaceNonAlphanumeric(@"-").Trim('-'); + + } + + + /// + /// Encodes a string to base64 using UTF-8 + /// + /// The input. + /// + [Pure] + public static string ToBase64(this string input) + { + if (string.IsNullOrWhiteSpace(input)) + return string.Empty; + var utf8Bytes = Encoding.UTF8.GetBytes(input); + return Convert.ToBase64String(utf8Bytes); + } + + /// + /// Decodes a base64 string using the specified encoding set + /// + /// The input. + /// The encoding. + /// + [Pure] + public static string FromBase64(this string input, string encoding = "ISO-8859-1") + { + if (string.IsNullOrWhiteSpace(input)) + return string.Empty; + var base64Bytes = Convert.FromBase64String(input); + return Encoding.GetEncoding(encoding).GetString(base64Bytes); + } + + /// + /// Converts a string from ISO-8859-2 encoding to UTF-8 encoding + /// + /// The input. + /// String. + [Pure] + public static string FromISO2UTF8(this string input) + { + if (string.IsNullOrWhiteSpace(input)) + return string.Empty; + + var iso = Encoding.GetEncoding("ISO-8859-1"); + var utf = Encoding.UTF8; + + var bytes = iso.GetBytes(input); + var utf8Bytes = Encoding.Convert(iso, utf, bytes); + return utf.GetString(utf8Bytes); + } + + /// + /// To the name of the valid file. + /// + /// Name of the file. + /// + [Pure] + public static string ToValidFileName(this string fileName) + { + return StringValidations.InvalidFileName.Replace(fileName, "_"); + } + + /// + /// A String extension method that calculates Levenshtein distance. + /// + /// The string to be compared. + /// The string to compare. + /// An Int32. + [Pure] + public static int Levenshtein(this string input, string inputToCompare) + { + var n = input.Length; + + var m = inputToCompare.Length; + + var d = new int[n + 1, m + 1]; + + if (n == 0) + return m; + + if (m == 0) + return n; + + for (var i = 0; i <= n; i++) + d[i, 0] = i; + + for (var j = 0; j <= m; j++) + d[0, j] = j; + + for (var i = 1; i <= n; i++) + { + for (var j = 1; j <= m; j++) + { + var cost = inputToCompare.Substring(j - 1, 1) == input.Substring(i - 1, 1) ? 0 : 1; + var min = Math.Min(d[i - 1, j] + 1, d[i, j - 1] + 1); + d[i, j] = Math.Min(min, d[i - 1, j - 1] + cost); + } + } + + return d[n, m]; + } + + /// + /// A String extension method that calculates levenshtein distance with invariant culture and insensitive case. + /// + /// The string to be compared. + /// The string to compare. + /// An Int32. + [Pure] + public static int LevenshteinInvariantCulture(this string input, string inputToCompare) + { + input = input.RemoveDiacritics().ToLower(); + return input.Levenshtein(inputToCompare.RemoveDiacritics().ToLower()); + } + + /// + /// Gets the name of the path or file. + /// + /// The URL. + /// + [Pure] + public static string GetPathOrFileName(this string url) + { + var uri = new Uri(url); + return string.IsNullOrWhiteSpace(uri.GetFileExtension()) ? uri.AbsolutePath : uri.GetFileName(); + } + + /// + /// An URI extension method that gets file name. + /// + /// The URI to act on. + /// The file name. + [Pure] + public static string GetFileName(this Uri uri) + { + return string.IsNullOrWhiteSpace(uri.LocalPath) ? string.Empty : Path.GetFileName(uri.LocalPath); + } + + /// + /// An URI extension method that gets file extension. + /// + /// The URI to act on. + /// The file extension. + [Pure] + public static string GetFileExtension(this Uri uri) + { + return uri.LocalPath.GetFileExtension(); + } + + /// + /// Gets the file extension. + /// + /// Name of the file. + /// String. + + [Pure] + public static string GetFileExtension(this string fileName) + { + return string.IsNullOrWhiteSpace(fileName) + ? string.Empty + : Path.GetExtension(fileName); + } + + /// + /// To the center. + /// + /// The input. + /// The spacer. + /// Size of the line. + /// String. + [Pure] + public static string ToCenter(this string input, char spacer, int lineSize) + { + var half = lineSize - input.Length / 2; + return $@"{new string(spacer, half)}{input}{new string(spacer, input.Length % 2 == 1 ? ++half : half)}"; + } + + /// + /// Determines whether the string is a valid JSON object + /// + /// The raw json string. + /// Boolean. + [Pure] + public static bool IsValidJson(this string jsonRaw) + { + try + { + jsonRaw = jsonRaw.Trim(); + + var isObject = jsonRaw.StartsWith(@"{") && jsonRaw.EndsWith(@"}"); + var isArray = jsonRaw.StartsWith(@"[") && jsonRaw.EndsWith(@"]"); + + if (!isObject && !isArray) + return false; + + JToken.Parse(jsonRaw); + + return true; + } + catch (Exception) + { + return false; + } + } + + /// + /// Split the string by chuck length, + /// + /// + /// + /// + [Pure] + public static IEnumerable SplitBy(this string str, int chunkLength) + { + if (string.IsNullOrEmpty(str)) + { + yield return string.Empty; + yield break; + } + if (chunkLength < 1) + throw new ArgumentException(@"Invalid chuck length", nameof(chunkLength)); + for (var i = 0; i < str.Length; i += chunkLength) + yield return str.Substring(i, chunkLength + i > str.Length ? str.Length - i : chunkLength); + } + + /// + /// Strips the tags. + /// + /// The source. + /// + [Pure] + public static string StripTags(this string source) + { + if (string.IsNullOrWhiteSpace(source)) + return string.Empty; + + var array = new char[source.Length]; + var arrayIndex = 0; + var inside = false; + + foreach (var let in source) + { + inside = StripTagInternal(@let, inside, array, ref arrayIndex); + } + + return new string(array, 0, arrayIndex); + } + + /// + /// Strips the tag internal. + /// + /// The let. + /// if set to true [inside]. + /// The array. + /// Index of the array. + /// true if XXXX, false otherwise. + private static bool StripTagInternal(char @let, bool inside, char[] array, ref int arrayIndex) + { + switch (@let) + { + case '<': + return true; + case '>': + return false; + default: + if (inside) + return true; + + array[arrayIndex] = @let; + arrayIndex++; + + break; + } + + return false; + } + } +} diff --git a/Src/CrispyWaffle/Extensions/TypeExtensions.cs b/Src/CrispyWaffle/Extensions/TypeExtensions.cs index 407603ea..1f2b5246 100644 --- a/Src/CrispyWaffle/Extensions/TypeExtensions.cs +++ b/Src/CrispyWaffle/Extensions/TypeExtensions.cs @@ -1,161 +1,175 @@ -namespace CrispyWaffle.Extensions -{ - using Attributes; - using System; - using System.Diagnostics; - using System.Linq; - using System.Reflection; - using Utilities; - - /// - /// Class TypeExtensions. - /// - public static class TypeExtensions - { - - /// - /// Gets the name of the query string builder key. - /// - /// The property information. - /// - public static string GetQueryStringBuilderKeyName(this PropertyInfo propertyInfo) - { - return propertyInfo.GetCustomAttributes(typeof(QueryStringBuilderKeyNameAttribute), false) is - QueryStringBuilderKeyNameAttribute[] attributes && attributes.Length > 0 - ? attributes[0].KeyName - : null; - } - - /// - /// Queries the string builder ignore. - /// - /// The property information. - /// - public static bool QueryStringBuilderIgnore(this PropertyInfo propertyInfo) - { - return propertyInfo.GetCustomAttributes(typeof(QueryStringBuilderIgnoreAttribute), false) is - QueryStringBuilderIgnoreAttribute[] attributes && attributes.Length > 0; - } - - /// - /// Updates the values. - /// - /// - /// The current object. - /// The new object. - /// T. - public static T UpdateValues(this T currentObject, T newObject) where T : IUpdateable - { - var type = currentObject.GetType(); - var defaultObject = (T)Activator.CreateInstance(type); - foreach (var propertyInfo in type.GetProperties()) - { - var defaultValue = propertyInfo.GetValue(defaultObject); - var newValue = propertyInfo.GetValue(newObject); - var currentValue = propertyInfo.GetValue(currentObject); - var shouldSerialize = false; - - var shouldSerializeMethod = type.GetMethod(string.Concat(@"ShouldSerialize", propertyInfo.Name)); - if (shouldSerializeMethod != null && shouldSerializeMethod.ReturnType == typeof(bool)) - shouldSerialize = (bool)shouldSerializeMethod.Invoke(newObject, null); - - if (!shouldSerialize && (newValue == null || newValue.Equals(defaultValue))) - continue; - - if (newValue == null || propertyInfo.PropertyType.IsSimpleType()) - propertyInfo.SetValue(currentObject, newValue); - else if (typeof(IUpdateable).IsAssignableFrom(propertyInfo.PropertyType)) - propertyInfo.SetValue(currentObject, ((IUpdateable)currentValue).UpdateValues((IUpdateable)newValue), null); - } - return currentObject; - } - - /// - /// Check whenever the type implements the - /// - /// - /// The source. - /// true if implements, false otherwise - public static bool Implements(this Type source) where TInterface : class - { - return typeof(TInterface).IsAssignableFrom(source); - } - - /// - /// Determines whether [is simple type] [the specified type]. - /// - /// The type. - /// Boolean. - public static bool IsSimpleType(this Type type) - { - return type.IsValueType || - type.IsPrimitive || - new[] { - typeof(string), - typeof(decimal), - typeof(DateTime), - typeof(DateTimeOffset), - typeof(TimeSpan), - typeof(Guid) - }.Contains(type) || - Convert.GetTypeCode(type) != TypeCode.Object; - } - - /// - /// Determines whether [is numeric type] [the specified type]. - /// - /// The type. - /// Boolean. - public static bool IsNumericType(this Type type) - { - while (true) - { - switch (Type.GetTypeCode(type)) - { - case TypeCode.Byte: - case TypeCode.SByte: - case TypeCode.UInt16: - case TypeCode.UInt32: - case TypeCode.UInt64: - case TypeCode.Int16: - case TypeCode.Int32: - case TypeCode.Int64: - case TypeCode.Decimal: - case TypeCode.Double: - case TypeCode.Single: - return true; - case TypeCode.Object: - if (type != null - && (!type.IsGenericType || - type.GetGenericTypeDefinition() != typeof(Nullable<>))) - return false; - if (type != null) - type = Nullable.GetUnderlyingType(type); - break; - default: - return false; - } - } - } - - /// - /// Gets the calling method. - /// - /// The frame. - /// if set to true [exclude begin]. - /// - public static string GetCallingMethod(int frame = 1, bool excludeBegin = true) - { - var stack = new StackTrace(); - var method = stack.GetFrame(frame).GetMethod(); - if (method == null) - return @"CrispyWaffle"; - var ns = method.DeclaringType?.FullName; - if (string.IsNullOrWhiteSpace(ns)) - return method.Name; - if (excludeBegin && ns.StartsWith(@"CrispyWaffle", StringExtensions.Comparison)) - ns = ns.Substring(13); - return $@"{ns}.{method.Name}"; - } - } -} +using System.Collections.Generic; + +namespace CrispyWaffle.Extensions +{ + using Attributes; + using System; + using System.Diagnostics; + using System.Linq; + using System.Reflection; + using Utilities; + + /// + /// Class TypeExtensions. + /// + public static class TypeExtensions + { + + /// + /// Gets the name of the query string builder key. + /// + /// The property information. + /// + public static string GetQueryStringBuilderKeyName(this PropertyInfo propertyInfo) + { + return propertyInfo.GetCustomAttributes(typeof(QueryStringBuilderKeyNameAttribute), false) is + QueryStringBuilderKeyNameAttribute[] attributes && attributes.Length > 0 + ? attributes[0].KeyName + : null; + } + + /// + /// Queries the string builder ignore. + /// + /// The property information. + /// + public static bool QueryStringBuilderIgnore(this PropertyInfo propertyInfo) + { + return propertyInfo.GetCustomAttributes(typeof(QueryStringBuilderIgnoreAttribute), false) is + QueryStringBuilderIgnoreAttribute[] attributes && attributes.Length > 0; + } + + /// + /// Updates the values. + /// + /// + /// The current object. + /// The new object. + /// T. + public static T UpdateValues(this T currentObject, T newObject) where T : IUpdateable + { + var type = currentObject.GetType(); + var defaultObject = (T)Activator.CreateInstance(type); + foreach (var propertyInfo in type.GetProperties()) + { + UpdateValueInternal(currentObject, newObject, propertyInfo, defaultObject, type); + } + return currentObject; + } + + /// + /// Updates the value internal. + /// + /// + /// The current object. + /// The new object. + /// The property information. + /// The default object. + /// The type. + private static void UpdateValueInternal(T currentObject, T newObject, PropertyInfo propertyInfo, T defaultObject, + Type type) where T : IUpdateable + { + var defaultValue = propertyInfo.GetValue(defaultObject); + var newValue = propertyInfo.GetValue(newObject); + var currentValue = propertyInfo.GetValue(currentObject); + var shouldSerialize = false; + + var shouldSerializeMethod = type.GetMethod(string.Concat(@"ShouldSerialize", propertyInfo.Name)); + if (shouldSerializeMethod != null && shouldSerializeMethod.ReturnType == typeof(bool)) + shouldSerialize = (bool)shouldSerializeMethod.Invoke(newObject, null); + + if (!shouldSerialize && (newValue == null || newValue.Equals(defaultValue))) + return; + + if (newValue == null || propertyInfo.PropertyType.IsSimpleType()) + propertyInfo.SetValue(currentObject, newValue); + else if (typeof(IUpdateable).IsAssignableFrom(propertyInfo.PropertyType)) + propertyInfo.SetValue(currentObject, ((IUpdateable)currentValue).UpdateValues((IUpdateable)newValue), null); + } + + /// + /// Check whenever the type implements the + /// + /// + /// The source. + /// true if implements, false otherwise + public static bool Implements(this Type source) where TInterface : class + { + return typeof(TInterface).IsAssignableFrom(source); + } + + /// + /// Determines whether [is simple type] [the specified type]. + /// + /// The type. + /// Boolean. + public static bool IsSimpleType(this Type type) + { + return type.IsValueType || + type.IsPrimitive || + new[] { + typeof(string), + typeof(decimal), + typeof(DateTime), + typeof(DateTimeOffset), + typeof(TimeSpan), + typeof(Guid) + }.Contains(type) || + Convert.GetTypeCode(type) != TypeCode.Object; + } + + /// + /// The primitive numeric types + /// + private static HashSet PrimitiveNumericTypes = new HashSet + { + TypeCode.Byte, TypeCode.SByte, TypeCode.UInt16, TypeCode.UInt32, TypeCode.UInt64, TypeCode.Int16, + TypeCode.Int32, TypeCode.Int64, TypeCode.Decimal, TypeCode.Double, TypeCode.Single + }; + + /// + /// Determines whether [is numeric type] [the specified type]. + /// + /// The type. + /// Boolean. + public static bool IsNumericType(this Type type) + { + while (true) + { + var typeCode = Type.GetTypeCode(type); + + if (PrimitiveNumericTypes.Contains(typeCode)) + return true; + + if (typeCode != TypeCode.Object) + return false; + + if (type != null && (!type.IsGenericType || type.GetGenericTypeDefinition() != typeof(Nullable<>))) + return false; + + if (type != null) + type = Nullable.GetUnderlyingType(type); + } + } + + /// + /// Gets the calling method. + /// + /// The frame. + /// if set to true [exclude begin]. + /// + public static string GetCallingMethod(int frame = 1, bool excludeBegin = true) + { + var stack = new StackTrace(); + var method = stack.GetFrame(frame).GetMethod(); + if (method == null) + return @"CrispyWaffle"; + var ns = method.DeclaringType?.FullName; + if (string.IsNullOrWhiteSpace(ns)) + return method.Name; + if (excludeBegin && ns.StartsWith(@"CrispyWaffle", StringExtensions.Comparison)) + ns = ns.Substring(13); + return $@"{ns}.{method.Name}"; + } + } +} diff --git a/Src/CrispyWaffle/Log/Handlers/DefaultExceptionHandler.cs b/Src/CrispyWaffle/Log/Handlers/DefaultExceptionHandler.cs index fc527479..8cb9d9c2 100644 --- a/Src/CrispyWaffle/Log/Handlers/DefaultExceptionHandler.cs +++ b/Src/CrispyWaffle/Log/Handlers/DefaultExceptionHandler.cs @@ -1,4 +1,6 @@ -namespace CrispyWaffle.Log.Handlers +using System.Reflection; + +namespace CrispyWaffle.Log.Handlers { using Composition; using Extensions; @@ -71,23 +73,48 @@ static DefaultExceptionHandler() private static string GetCategory() { var stack = new StackTrace(); + var counter = 1; + while (true) { var method = stack.GetFrame(counter++).GetMethod(); + if (method == null) return @"CrispyWaffle"; - var ns = method.DeclaringType?.FullName; - if (string.IsNullOrWhiteSpace(ns)) - return method.Name; - if (ns.StartsWith(@"CrispyWaffle.Log")) - continue; - if (ns.StartsWith(@"CrispyWaffle.", StringExtensions.Comparison)) - ns = ns.Substring(13); - return ns; + + if (GetNamespace(method, out var category)) + return category; } } + /// + /// Gets the namespace. + /// + /// The method. + /// The category. + /// true if XXXX, false otherwise. + private static bool GetNamespace(MethodBase method, out string category) + { + category = string.Empty; + var ns = method.DeclaringType?.FullName; + + if (string.IsNullOrWhiteSpace(ns)) + { + category = method.Name; + return true; + } + + if (ns.StartsWith(@"CrispyWaffle.Log")) + return false; + + if (ns.StartsWith(@"CrispyWaffle.", StringExtensions.Comparison)) + ns = ns.Substring(13); + + category = ns; + return true; + } + /// /// Handles the internal. /// @@ -102,7 +129,7 @@ private static void HandleInternal(Exception exception) TelemetryAnalytics.TrackException(type); var messages = exceptions.GetMessages(category, AdditionalProviders.Where(p => p.Item2 == ExceptionLogType.MESSAGE).Select(p => p.Item1).ToList()); - + foreach (var additionalProvider in AdditionalProviders.Where(p => p.Item2 == ExceptionLogType.FULL)) additionalProvider.Item1.Error(category, messages); } diff --git a/Src/CrispyWaffle/Log/LogConsumer.cs b/Src/CrispyWaffle/Log/LogConsumer.cs index 12c1d795..e3d1a25d 100644 --- a/Src/CrispyWaffle/Log/LogConsumer.cs +++ b/Src/CrispyWaffle/Log/LogConsumer.cs @@ -1,4 +1,6 @@ -namespace CrispyWaffle.Log +using System.Reflection; + +namespace CrispyWaffle.Log { using Composition; using Extensions; @@ -94,20 +96,37 @@ private static string GetCategory() if (method == null) return @"CrispyWaffle"; - var ns = method.DeclaringType?.FullName; + if (GetNamespace(method, out var category)) + return category; + } + } - if (string.IsNullOrWhiteSpace(ns)) - return method.Name; + /// + /// Gets the namespace. + /// + /// The method. + /// The category. + /// true if XXXX, false otherwise. + private static bool GetNamespace(MethodBase method, out string category) + { + category = string.Empty; + var ns = method.DeclaringType?.FullName; - if (ns.StartsWith(@"CrispyWaffle.Log") || - ns.StartsWith(@"CrispyWaffle") && ns.EndsWith(@"LogProvider")) - continue; + if (string.IsNullOrWhiteSpace(ns)) + { + category = method.Name; + return true; + } - if (ns.StartsWith(@"CrispyWaffle.", StringExtensions.Comparison)) - ns = ns.Substring(13); + if (ns.StartsWith(@"CrispyWaffle.Log") || + ns.StartsWith(@"CrispyWaffle") && ns.EndsWith(@"LogProvider")) + return false; - return ns; - } + if (ns.StartsWith(@"CrispyWaffle.", StringExtensions.Comparison)) + ns = ns.Substring(13); + + category = ns; + return true; } #endregion @@ -165,12 +184,32 @@ public static void SetHandler(IExceptionHandler handler) public static bool LogTo(LogLevel level, string message) where TLogProvider : ILogProvider { var type = typeof(TLogProvider); + var provider = Providers.SingleOrDefault(p => type == p.GetType()); + if (provider == null) return false; + var category = GetCategory(); + if (Filters.Any(f => !f.Filter(type.FullName, level, category, message))) return false; + + LogToInternal(level, message, provider, category); + + return true; + } + + /// + /// Logs to internal. + /// + /// The level. + /// The message. + /// The provider. + /// The category. + /// level - null + private static void LogToInternal(LogLevel level, string message, ILogProvider provider, string category) + { switch (level) { case LogLevel.FATAL: @@ -194,7 +233,6 @@ public static bool LogTo(LogLevel level, string message) where TLo default: throw new ArgumentOutOfRangeException(nameof(level), level, null); } - return true; } /// diff --git a/Src/CrispyWaffle/Serialization/Adapters/BinarySerializerAdapter.cs b/Src/CrispyWaffle/Serialization/Adapters/BinarySerializerAdapter.cs index bd264cb6..ff86dc09 100644 --- a/Src/CrispyWaffle/Serialization/Adapters/BinarySerializerAdapter.cs +++ b/Src/CrispyWaffle/Serialization/Adapters/BinarySerializerAdapter.cs @@ -1,150 +1,172 @@ -namespace CrispyWaffle.Serialization.Adapters -{ - using System; - using System.Diagnostics.Contracts; - using System.IO; - using System.Runtime.Serialization; - using System.Runtime.Serialization.Formatters.Binary; - using System.Text; - using System.Threading; - - /// - /// Class BinarySerializerAdapter. This class cannot be inherited. - /// - /// - public sealed class BinarySerializerAdapter : ISerializerAdapter - { - #region Implemetation of ISerializerAdapter - - /// - /// Deserialize a stream to a generic type - /// - /// Generic type parameter - /// The serialized object as stream. - /// (Optional) Determines the encoding to read the stream (not used in BinarySerializerProvider) - /// A T. - [Pure] - public T DeserializeFromStream(Stream stream, Encoding encoding = null) where T : class - { - var formatter = new BinaryFormatter(); - var result = formatter.Deserialize(stream); - stream.Close(); - return (T)result; - } - - /// - /// Deserializes. - /// - /// Generic type parameter. - /// The serialized. - /// A T. - [Pure] - public T Deserialize(object serialized) where T : class - { - return DeserializeFromStream((Stream)serialized); - } - - /// - /// Loads the given file and Deserialize its. - /// - /// Generic type parameter. - /// The file. - /// A T. - /// file - Supply a valid filename - /// - [Pure] - public T Load(string file) where T : class - { - if (string.IsNullOrWhiteSpace(file)) - throw new ArgumentNullException(nameof(file), "Supply a valid filename"); - - if (!File.Exists(file)) - throw new LocalFileNotFoundException(file, Path.GetDirectoryName(Path.GetFullPath(file))); - - var fileName = Path.GetFileName(file); - var folder = Path.GetDirectoryName(file); - var are = new AutoResetEvent(false); - - Stream stream; - while (true) - { - try - { - stream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.None); - break; - } - catch (IOException) - { - FileSystemWatcher watcher = null; - try - { - watcher = new FileSystemWatcher { Filter = fileName, Path = folder, NotifyFilter = NotifyFilters.Attributes | NotifyFilters.DirectoryName | NotifyFilters.FileName | NotifyFilters.LastWrite | NotifyFilters.Size, EnableRaisingEvents = true }; - watcher.Changed += (o, e) => are.Set(); - are.WaitOne(new TimeSpan(0, 0, 0, 30)); - } - finally - { - watcher?.Dispose(); - } - } - } - return Deserialize(stream); - } - - /// - /// Serializes. - /// - /// Generic type parameter. - /// The deserialized. - /// [out] The stream. - public void Serialize(T deserialized, out Stream stream) where T : class - { - stream = new MemoryStream(); - - using (var streamTemp = new MemoryStream()) - { - - IFormatter formatter = new BinaryFormatter(); - - formatter.Serialize(streamTemp, deserialized); - - streamTemp.Seek(0, SeekOrigin.Begin); - streamTemp.CopyTo(stream); - stream.Seek(0, SeekOrigin.Begin); - } - } - - /// - /// Serialize the deserialized Object and Saves the given file. - /// - /// Generic type parameter. - /// The file. - /// The deserialized. - /// file - Supply a valid filename - public void Save(string file, T deserialized) where T : class - { - Stream stream = null; - try - { - if (string.IsNullOrWhiteSpace(file)) - throw new ArgumentNullException(nameof(file), "Supply a valid filename"); - - if (File.Exists(file)) - File.Delete(file); - - using (var fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None)) - { - Serialize(deserialized, out stream); - - stream.CopyTo(fileStream); - } - } - finally - { - stream?.Dispose(); - } - } - - #endregion - } -} +namespace CrispyWaffle.Serialization.Adapters +{ + using System; + using System.Diagnostics.Contracts; + using System.IO; + using System.Runtime.Serialization; + using System.Runtime.Serialization.Formatters.Binary; + using System.Text; + using System.Threading; + + /// + /// Class BinarySerializerAdapter. This class cannot be inherited. + /// + /// + public sealed class BinarySerializerAdapter : ISerializerAdapter + { + #region Implemetation of ISerializerAdapter + + /// + /// Deserialize a stream to a generic type + /// + /// Generic type parameter + /// The serialized object as stream. + /// (Optional) Determines the encoding to read the stream (not used in BinarySerializerProvider) + /// A T. + [Pure] + public T DeserializeFromStream(Stream stream, Encoding encoding = null) where T : class + { + var formatter = new BinaryFormatter(); + var result = formatter.Deserialize(stream); + stream.Close(); + return (T)result; + } + + /// + /// Deserializes. + /// + /// Generic type parameter. + /// The serialized. + /// A T. + [Pure] + public T Deserialize(object serialized) where T : class + { + return DeserializeFromStream((Stream)serialized); + } + + /// + /// Loads the given file and Deserialize its. + /// + /// Generic type parameter. + /// The file. + /// A T. + /// file - Supply a valid filename + /// + [Pure] + public T Load(string file) where T : class + { + if (string.IsNullOrWhiteSpace(file)) + throw new ArgumentNullException(nameof(file), "Supply a valid filename"); + + if (!File.Exists(file)) + throw new LocalFileNotFoundException(file, Path.GetDirectoryName(Path.GetFullPath(file))); + + var fileName = Path.GetFileName(file); + var folder = Path.GetDirectoryName(file); + var are = new AutoResetEvent(false); + + var stream = LoadInternal(file, fileName, folder, are); + + return Deserialize(stream); + } + + /// + /// Loads the internal. + /// + /// The file. + /// Name of the file. + /// The folder. + /// The are. + /// Stream. + private static Stream LoadInternal(string file, string fileName, string folder, AutoResetEvent are) + { + Stream stream; + while (true) + { + try + { + stream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.None); + break; + } + catch (IOException) + { + FileSystemWatcher watcher = null; + try + { + watcher = new FileSystemWatcher + { + Filter = fileName, Path = folder, + NotifyFilter = NotifyFilters.Attributes | NotifyFilters.DirectoryName | NotifyFilters.FileName | + NotifyFilters.LastWrite | NotifyFilters.Size, + EnableRaisingEvents = true + }; + watcher.Changed += (o, e) => are.Set(); + are.WaitOne(new TimeSpan(0, 0, 0, 30)); + } + finally + { + watcher?.Dispose(); + } + } + } + + return stream; + } + + /// + /// Serializes. + /// + /// Generic type parameter. + /// The deserialized. + /// [out] The stream. + public void Serialize(T deserialized, out Stream stream) where T : class + { + stream = new MemoryStream(); + + using (var streamTemp = new MemoryStream()) + { + + IFormatter formatter = new BinaryFormatter(); + + formatter.Serialize(streamTemp, deserialized); + + streamTemp.Seek(0, SeekOrigin.Begin); + streamTemp.CopyTo(stream); + stream.Seek(0, SeekOrigin.Begin); + } + } + + /// + /// Serialize the deserialized Object and Saves the given file. + /// + /// Generic type parameter. + /// The file. + /// The deserialized. + /// file - Supply a valid filename + public void Save(string file, T deserialized) where T : class + { + Stream stream = null; + try + { + if (string.IsNullOrWhiteSpace(file)) + throw new ArgumentNullException(nameof(file), "Supply a valid filename"); + + if (File.Exists(file)) + File.Delete(file); + + using (var fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None)) + { + Serialize(deserialized, out stream); + + stream.CopyTo(fileStream); + } + } + finally + { + stream?.Dispose(); + } + } + + #endregion + } +} diff --git a/Src/CrispyWaffle/Serialization/SerializerFactory.cs b/Src/CrispyWaffle/Serialization/SerializerFactory.cs index 341cc264..c21dd90e 100644 --- a/Src/CrispyWaffle/Serialization/SerializerFactory.cs +++ b/Src/CrispyWaffle/Serialization/SerializerFactory.cs @@ -1,181 +1,226 @@ -namespace CrispyWaffle.Serialization -{ - using Adapters; - using Composition; - using Newtonsoft.Json; - using System; - using System.Collections; - using System.Diagnostics; - using System.Diagnostics.Contracts; - using System.Globalization; - - /// - /// The serializer factory class. - /// - [DebuggerStepThrough] - public static class SerializerFactory - { - /// - /// Gets serializer from type. - /// - /// - /// - /// Thrown when the requested operation is invalid. - /// - /// - /// - /// Generic type parameter. - /// - /// - /// The object. - /// - /// - /// - /// The serializer from type< t> - /// - /// - /// - [Pure] - private static SerializerConverter GetSerializerFromType(T obj) where T : class - { - var type = obj.GetType(); - if (!type.IsClass) - throw new InvalidOperationException($"Unable to serializer the object of type '{type.FullName}'"); - if (Attribute.GetCustomAttribute(type, typeof(SerializerAttribute)) is SerializerAttribute attribute) - return GetSerializer(obj, attribute); - if (type.IsArray) - { - type = type.GetElementType(); - attribute = Attribute.GetCustomAttribute(type ?? throw new InvalidOperationException(), typeof(SerializerAttribute)) as SerializerAttribute; - if (attribute != null) - return GetSerializer(obj, attribute); - } - else if (typeof(IEnumerable).IsAssignableFrom(type)) - { - type = type.GetGenericArguments()[0]; - attribute = Attribute.GetCustomAttribute(type, typeof(SerializerAttribute)) as SerializerAttribute; - if (attribute != null) - return GetSerializer(obj, attribute); - } - if (type.IsSerializable) - return GetSerializer(obj, new SerializerAttribute(SerializerFormat.BINARY)); - throw new InvalidOperationException( - $"The {typeof(SerializerAttribute).FullName} attribute was not found in the object of type {type.FullName}"); - } - - /// - /// Gets the serializer. - /// - /// - /// - /// Generic type parameter. - /// - /// - /// The object. - /// - /// - /// - /// The serializer< t> - /// - [Pure] - public static SerializerConverter GetSerializer(this T obj) where T : class - { - return GetSerializerFromType(obj); - } - - /// - /// Gets the serializer. - /// - /// - /// - /// Generic type parameter. - /// - /// - /// - /// The serializer< t> - /// - [Pure] - public static SerializerConverter GetSerializer() where T : class - { - var obj = (T)Activator.CreateInstance(typeof(T)); - return GetSerializerFromType(obj); - } - - /// - /// Gets the serializer. - /// - /// - /// The object. - /// The attribute. - /// - /// format - [Pure] - private static SerializerConverter GetSerializer(T obj, SerializerAttribute attribute) where T : class - { - switch (attribute.Format) - { - case SerializerFormat.BINARY: - return new SerializerConverter(obj, ServiceLocator.Resolve()); - case SerializerFormat.JSON when attribute.IsStrict: - return new SerializerConverter(obj, ServiceLocator.Resolve()); - case SerializerFormat.JSON when !attribute.IsStrict: - var settings = new JsonSerializerSettings - { - Converters = { new NotNullObserverConverter() }, - Culture = CultureInfo.GetCultureInfo("pt-br"), - MissingMemberHandling = MissingMemberHandling.Ignore, - NullValueHandling = NullValueHandling.Ignore - }; - return new SerializerConverter(obj, new JsonSerializerAdapter(settings)); - case SerializerFormat.XML: - return new SerializerConverter(obj, ServiceLocator.Resolve()); - default: - throw new InvalidOperationException(nameof(attribute.Format)); - } - } - - /// - /// A T extension method that gets custom serializer. - /// - /// - /// - /// Generic type parameter. - /// - /// - /// The object. - /// - /// - /// Describes the format to use. - /// - /// - /// - /// The custom serializer< t> - /// - [Pure] - public static SerializerConverter GetCustomSerializer(this T obj, SerializerFormat format) where T : class - { - return GetSerializer(obj, new SerializerAttribute(format)); - } - - /// - /// A T extension method that gets custom serializer. - /// - /// - /// - /// Generic type parameter. - /// - /// - /// Describes the format to use. - /// - /// - /// - /// The custom serializer< t> - /// - [Pure] - public static SerializerConverter GetCustomSerializer(SerializerFormat format) where T : class - { - var obj = (T)Activator.CreateInstance(typeof(T)); - return GetCustomSerializer(obj, format); - } - } -} +namespace CrispyWaffle.Serialization +{ + using Adapters; + using Composition; + using Newtonsoft.Json; + using System; + using System.Collections; + using System.Diagnostics; + using System.Diagnostics.Contracts; + using System.Globalization; + + /// + /// The serializer factory class. + /// + [DebuggerStepThrough] + public static class SerializerFactory + { + /// + /// Gets the type of the serializer from. + /// + /// + /// The object. + /// SerializerConverter<T>. + /// Unable to serializer the object of type '{type.FullName}' + /// Invalid array subtype + /// The {typeof(SerializerAttribute).FullName} attribute was not found in the object of type {type.FullName} + [Pure] + private static SerializerConverter GetSerializerFromType(T obj) where T : class + { + var type = obj.GetType(); + + if (!type.IsClass) + throw new InvalidOperationException($"Unable to serializer the object of type '{type.FullName}'"); + + if (Attribute.GetCustomAttribute(type, typeof(SerializerAttribute)) is SerializerAttribute attribute) + return GetSerializer(obj, attribute); + + if (GetSerializerFromArrayOrInherit(obj, ref type, out var serializerConverter)) + return serializerConverter; + + if (type.IsSerializable) + return GetSerializer(obj, new SerializerAttribute(SerializerFormat.BINARY)); + + throw new InvalidOperationException($"The {typeof(SerializerAttribute).FullName} attribute was not found in the object of type {type.FullName}"); + } + + /// + /// Gets the serializer from array or inherit. + /// + /// + /// The object. + /// The type. + /// The serializer converter. + /// true if XXXX, false otherwise. + /// Invalid array subtype + private static bool GetSerializerFromArrayOrInherit(T obj, ref Type type, + out SerializerConverter serializerConverter) where T : class + { + serializerConverter = null; + + if (type.IsArray) + { + return GetSerializerFromArray(obj, ref type, ref serializerConverter); + } + + return typeof(IEnumerable).IsAssignableFrom(type) && GetSerializerFromInherit(obj, ref type, ref serializerConverter); + } + + /// + /// Gets the serializer from inherit. + /// + /// + /// The object. + /// The type. + /// The serializer converter. + /// true if XXXX, false otherwise. + private static bool GetSerializerFromInherit(T obj, ref Type type, ref SerializerConverter serializerConverter) + where T : class + { + type = type.GetGenericArguments()[0]; + + var attribute = Attribute.GetCustomAttribute(type, typeof(SerializerAttribute)) as SerializerAttribute; + + if (attribute == null) + return false; + + serializerConverter = GetSerializer(obj, attribute); + + return true; + } + + /// + /// Gets the serializer from array. + /// + /// + /// The object. + /// The type. + /// The serializer converter. + /// true if XXXX, false otherwise. + /// Invalid array subtype + private static bool GetSerializerFromArray(T obj, ref Type type, ref SerializerConverter serializerConverter) + where T : class + { + type = type.GetElementType(); + + var attribute = Attribute.GetCustomAttribute(type ?? throw new InvalidOperationException("Invalid array subtype"), + typeof(SerializerAttribute)) as SerializerAttribute; + + if (attribute == null) + return false; + + serializerConverter = GetSerializer(obj, attribute); + + return true; + } + + /// + /// Gets the serializer. + /// + /// + /// + /// Generic type parameter. + /// + /// + /// The object. + /// + /// + /// + /// The serializer< t> + /// + [Pure] + public static SerializerConverter GetSerializer(this T obj) where T : class + { + return GetSerializerFromType(obj); + } + + /// + /// Gets the serializer. + /// + /// + /// SerializerConverter<T>. + [Pure] + public static SerializerConverter GetSerializer() where T : class + { + var obj = (T)Activator.CreateInstance(typeof(T)); + return GetSerializerFromType(obj); + } + + /// + /// Gets the serializer. + /// + /// + /// The object. + /// The attribute. + /// + /// format + [Pure] + private static SerializerConverter GetSerializer(T obj, SerializerAttribute attribute) where T : class + { + switch (attribute.Format) + { + case SerializerFormat.BINARY: + return new SerializerConverter(obj, ServiceLocator.Resolve()); + case SerializerFormat.JSON when attribute.IsStrict: + return new SerializerConverter(obj, ServiceLocator.Resolve()); + case SerializerFormat.JSON when !attribute.IsStrict: + var settings = new JsonSerializerSettings + { + Converters = { new NotNullObserverConverter() }, + Culture = CultureInfo.GetCultureInfo("pt-br"), + MissingMemberHandling = MissingMemberHandling.Ignore, + NullValueHandling = NullValueHandling.Ignore + }; + return new SerializerConverter(obj, new JsonSerializerAdapter(settings)); + case SerializerFormat.XML: + return new SerializerConverter(obj, ServiceLocator.Resolve()); + default: + throw new InvalidOperationException(nameof(attribute.Format)); + } + } + + /// + /// A T extension method that gets custom serializer. + /// + /// + /// + /// Generic type parameter. + /// + /// + /// The object. + /// + /// + /// Describes the format to use. + /// + /// + /// + /// The custom serializer< t> + /// + [Pure] + public static SerializerConverter GetCustomSerializer(this T obj, SerializerFormat format) where T : class + { + return GetSerializer(obj, new SerializerAttribute(format)); + } + + /// + /// A T extension method that gets custom serializer. + /// + /// + /// + /// Generic type parameter. + /// + /// + /// Describes the format to use. + /// + /// + /// + /// The custom serializer< t> + /// + [Pure] + public static SerializerConverter GetCustomSerializer(SerializerFormat format) where T : class + { + var obj = (T)Activator.CreateInstance(typeof(T)); + return GetCustomSerializer(obj, format); + } + } +} diff --git a/Src/CrispyWaffle/Validations/PersonalDataValidation.cs b/Src/CrispyWaffle/Validations/PersonalDataValidation.cs index 9632e264..eee0293a 100644 --- a/Src/CrispyWaffle/Validations/PersonalDataValidation.cs +++ b/Src/CrispyWaffle/Validations/PersonalDataValidation.cs @@ -62,6 +62,26 @@ public static bool IsValidBrazilianCorporationDocument(this string document) return digits.Length == 2 && document.EndsWith(digits); } + /// + /// Calculates the module11. + /// + /// The working. + /// The multiplier. + /// System.Int32. + private static int CalculateModule11(string working, int[] multiplier) + { + var sum = 0; + + for (var i = 0; i < multiplier.Length; i++) + sum += working[i].ToString(StringExtensions.Culture).ToInt32() * multiplier[i]; + + var rest = sum % 11; + + rest = rest < 2 ? 0 : 11 - rest; + + return rest; + } + /// /// Calculates the brazilian person document digits. /// @@ -84,27 +104,13 @@ public static string CalculateBrazilianPersonDocumentDigits(this string document var working = document.Substring(0, 9); - var sum = 0; - - for (var i = 0; i < 9; i++) - sum += working[i].ToString(StringExtensions.Culture).ToInt32() * multiplierA[i]; - - var rest = sum % 11; - - rest = rest < 2 ? 0 : 11 - rest; + var rest = CalculateModule11(working, multiplierA); var digit = rest.ToString(StringExtensions.Culture); working = string.Concat(working, digit); - sum = 0; - - for (var i = 0; i < 10; i++) - sum += working[i].ToString(StringExtensions.Culture).ToInt32() * multiplierB[i]; - - rest = sum % 11; - - rest = rest < 2 ? 0 : 11 - rest; + rest = CalculateModule11(working, multiplierB); digit = string.Concat(digit, rest); @@ -127,29 +133,18 @@ public static string CalculateBrazilianCorporationDocument(this string document) if (document.Length != 14) throw new InvalidDocumentException("CNPJ", document); - var working = document.Substring(0, 12); - - var sum = 0; - - for (var i = 0; i < 12; i++) - sum += int.Parse(working[i].ToString(StringExtensions.Culture)) * multiplierA[i]; + if (SameNumberDocumentPattern.IsMatch(document)) + throw new InvalidDocumentException("CNPJ", document); - var rest = sum % 11; + var working = document.Substring(0, 12); - rest = rest < 2 ? 0 : 11 - rest; + var rest = CalculateModule11(working, multiplierA); var digit = rest.ToString(StringExtensions.Culture); - working = working + digit; - - sum = 0; - - for (var i = 0; i < 13; i++) - sum += int.Parse(working[i].ToString(StringExtensions.Culture)) * multiplierB[i]; - - rest = sum % 11; + working = string.Concat(working, digit); - rest = rest < 2 ? 0 : 11 - rest; + rest = CalculateModule11(working, multiplierB); digit = string.Concat(digit, rest);