Repository: asyncapi/modelina
Pull request #725 (2022-04-22 22:45:51)

refactor: simplified constraints and type mapping
**Description**
This PR finalizes the constraint and type mapping behavior.


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=asyncapi_generator-model-sdk&pullRequest=725)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=725) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=725&metric=duplicated_lines_density&view=list) No Duplication information


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=asyncapi_generator-model-sdk&pullRequest=725)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=725) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=725&metric=duplicated_lines_density&view=list) No Duplication information


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=asyncapi_generator-model-sdk&pullRequest=725)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=725) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=725&metric=duplicated_lines_density&view=list) No Duplication information


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=asyncapi_generator-model-sdk&pullRequest=725)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=725) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=725&metric=duplicated_lines_density&view=list) No Duplication information


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=asyncapi_generator-model-sdk&pullRequest=725)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=725) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=725&metric=duplicated_lines_density&view=list) No Duplication information


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=asyncapi_generator-model-sdk&pullRequest=725)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=725) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=725&metric=duplicated_lines_density&view=list) No Duplication information


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=asyncapi_generator-model-sdk&pullRequest=725)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=725) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=725&metric=duplicated_lines_density&view=list) No Duplication information


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=asyncapi_generator-model-sdk&pullRequest=725)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=725) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=725&metric=duplicated_lines_density&view=list) No Duplication information


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=asyncapi_generator-model-sdk&pullRequest=725)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=725&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=725) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=725&metric=duplicated_lines_density&view=list) No Duplication information



Patch:
diff --git a/docs/advanced.md b/docs/advanced.md
index db69b84c..f35ad0ae 100644
--- a/docs/advanced.md
+++ b/docs/advanced.md
@@ -13,8 +13,8 @@ This document contains many of the advanced use-cases that you may stumble upon
 - [Build your own model renderer](#build-your-own-model-renderer)
 - [Add logging to library](#add-logging-to-library)
 - [Change the generated indentation type and size](#change-the-generated-indentation-type-and-size)
-- [Change the naming format for properties](#change-the-naming-format-for-properties)
-- [Change the naming format for data models](#change-the-naming-format-for-data-models)
+- [Change the type mapping](#change-the-type-mapping)
+- [Changing the constrain rules](#changing-the-constrain-rules)
 
 <!-- tocstop -->
 
@@ -72,8 +72,16 @@ In some scenarios, depending on how you stitch them together, you might need to
 
 Check out this [example out for a live demonstration](../examples/indentation-type-and-size).
 
-## Change the naming format for properties
-TODO 
+## Change the type mapping
+Sometimes, the default type mapping simply dont use the types you expected, or fit into your use-case. Thats why the entire mapping from [MetaModels](./processing.md#the-meta-model) to constrained types can be altered.
+
+Check out this [example out for a live demonstration](../examples/change-type-mapping).
+
+## Changing the constrain rules
+When moving from a [MetaModel](./processing.md#the-meta-model) to a [ConstrainedMetaModel](./processing.md#the-constrained-meta-model) it means we bind the input to a specific output. That output has specific constraints that the input MUST adhere to, [read more about this here](constraints.md).
+
+There can be multiple reasons why you want to change the default constrain rules, and therefore you can form them to your needs.
+
+Check out this [example out for a live demonstration](../examples/overwrite-default-constraint/) for how to overwrite the default constraints.
 
-## Change the naming format for data models
-TODO
+Check out this [example out for a live demonstration](../examples/overwrite-naming-formatting/) for how to overwrite the naming formatting for models.
diff --git a/docs/usage.md b/docs/usage.md
index 16857853..56cc1ee3 100644
--- a/docs/usage.md
+++ b/docs/usage.md
@@ -14,8 +14,8 @@ For more specific integration options, please check out the [integration documen
 - [Generate models from AsyncAPI documents](#generate-models-from-asyncapi-documents)
 - [Generate models from JSON Schema documents](#generate-models-from-json-schema-documents)
 - [Generate models from Swagger 2.0 documents](#generate-models-from-swagger-20-documents)
-- [Generate models from OpenAPI documents](#generate-models-from-openapi-documents)
 - [Generate models from Meta models](#generate-models-from-meta-models)
+- [Generate models from OpenAPI documents](#generate-models-from-openapi-documents)
 - [Generate Go models](#generate-go-models)
 - [Generate C# models](#generate-c%23-models)
 - [Generate Java models](#generate-java-models)
diff --git a/examples/change-type-mapping/index.ts b/examples/change-type-mapping/index.ts
index a0e9755d..fb0d8fb4 100644
--- a/examples/change-type-mapping/index.ts
+++ b/examples/change-type-mapping/index.ts
@@ -3,7 +3,7 @@ import { TypeScriptGenerator } from '../../src';
 const generator = new TypeScriptGenerator({
   constraints: {
     typeMapping: {
-      Number: () => {
+      Number: ({renderer, model}) => {
         return 'integer';
       }
     }
diff --git a/src/generators/AbstractGenerator.ts b/src/generators/AbstractGenerator.ts
index 8eb722e6..b74c99b4 100644
--- a/src/generators/AbstractGenerator.ts
+++ b/src/generators/AbstractGenerator.ts
@@ -9,14 +9,14 @@ export interface CommonGeneratorOptions<P extends Preset = Preset> {
   };
   defaultPreset?: P;
   presets?: Presets<P>;
-  processorOptions?: ProcessorOptions
+  processorOptions?: ProcessorOptions;
 }
 
 export const defaultGeneratorOptions: CommonGeneratorOptions = {
   indentation: {
     type: IndentationTypes.SPACES,
     size: 2,
-  }
+  },
 };
 
 /**
diff --git a/src/generators/AbstractRenderer.ts b/src/generators/AbstractRenderer.ts
index 7d14936f..a2596378 100644
--- a/src/generators/AbstractRenderer.ts
+++ b/src/generators/AbstractRenderer.ts
@@ -10,7 +10,7 @@ export abstract class AbstractRenderer<
   G extends AbstractGenerator = AbstractGenerator
 > {
   constructor(
-    protected readonly options: O,
+    readonly options: O,
     readonly generator: G,
     protected readonly presets: Array<[Preset, unknown]>,
     protected readonly model: CommonModel, 
diff --git a/src/generators/java/JavaConstrainer.ts b/src/generators/java/JavaConstrainer.ts
index 1ea0e234..975cdee0 100644
--- a/src/generators/java/JavaConstrainer.ts
+++ b/src/generators/java/JavaConstrainer.ts
@@ -1,86 +1,33 @@
-/* eslint-disable no-fallthrough */
-import { ConstrainedAnyModel, ConstrainedBooleanModel, ConstrainedFloatModel, ConstrainedIntegerModel, ConstrainedMetaModel, ConstrainedObjectModel, ConstrainedReferenceModel, ConstrainedStringModel, ConstrainedTupleModel, ConstrainedTupleValueModel, ConstrainedArrayModel, ConstrainedUnionModel, ConstrainedEnumModel, ConstrainedDictionaryModel, ConstrainedEnumValueModel } from '../../models/ConstrainedMetaModel';
-import { AnyModel, BooleanModel, FloatModel, IntegerModel, MetaModel, ObjectModel, ReferenceModel, StringModel, TupleModel, ArrayModel, UnionModel, EnumModel, DictionaryModel } from '../../models/MetaModel';
-import { defaultPropertyKeyConstraints, PropertyKeyConstraintType } from './constrainer/PropertyKeyConstrainer';
-import { defaultModelNameConstraints, ModelNameConstraintType } from './constrainer/ModelNameConstrainer';
-import { defaultEnumKeyConstraints, EnumConstraintType } from './constrainer/EnumConstrainer';
-import { JavaOptions } from './JavaGenerator';
+import { TypeMapping } from '../../helpers';
+import { defaultEnumKeyConstraints, defaultEnumValueConstraints } from './constrainer/EnumConstrainer';
+import { defaultModelNameConstraints } from './constrainer/ModelNameConstrainer';
+import { defaultPropertyKeyConstraints } from './constrainer/PropertyKeyConstrainer';
+import { JavaRenderer } from './JavaRenderer';
 
-export interface JavaConstraints {
-  enumKey: EnumConstraintType,
-  modelName: ModelNameConstraintType,
-  propertyKey: PropertyKeyConstraintType,
-}
-
-export const DefaultJavaConstraints: JavaConstraints = {
-  enumKey: defaultEnumKeyConstraints(),
-  modelName: defaultModelNameConstraints(),
-  propertyKey: defaultPropertyKeyConstraints()
-};
-
-type TypeMappingFunction<T extends ConstrainedMetaModel> = (model: T) => string;
-
-type TypeMapping = {
-  Object?: TypeMappingFunction<ConstrainedObjectModel>,
-  Reference?: TypeMappingFunction<ConstrainedReferenceModel>,
-  Any?: TypeMappingFunction<ConstrainedAnyModel>,
-  Float?: TypeMappingFunction<ConstrainedFloatModel>,
-  Integer?: TypeMappingFunction<ConstrainedIntegerModel>,
-  String?: TypeMappingFunction<ConstrainedStringModel>,
-  Boolean?: TypeMappingFunction<ConstrainedBooleanModel>,
-  Tuple?: TypeMappingFunction<ConstrainedTupleModel>,
-  Array?: TypeMappingFunction<ConstrainedArrayModel>,
-  Enum?: TypeMappingFunction<ConstrainedEnumModel>,
-  Union?: TypeMappingFunction<ConstrainedUnionModel>,
-  Dictionary?: TypeMappingFunction<ConstrainedDictionaryModel>
-}
-
-function constrainReferenceModel(constrainedName: string, metaModel: ReferenceModel, typeMapping: TypeMapping, constrainRules: JavaConstraints, options: JavaOptions): ConstrainedReferenceModel {
-  const constrainedRefModel = constrainMetaModel(metaModel.ref, typeMapping, constrainRules, options);
-  const constrainedModel = new ConstrainedReferenceModel(constrainedName, metaModel.originalInput, '', constrainedRefModel);
-  if (typeMapping.Reference !== undefined) {
-    constrainedModel.type = typeMapping.Reference(constrainedModel);
-  } else {
-    constrainedModel.type = constrainedModel.name;
-  }
-  return constrainedModel;
-}
-function constrainAnyModel(constrainedName: string, metaModel: AnyModel, typeMapping: TypeMapping): ConstrainedAnyModel {
-  const constrainedModel = new ConstrainedAnyModel(constrainedName, metaModel.originalInput, '');
-  if (typeMapping.Any !== undefined) {
-    constrainedModel.type = typeMapping.Any(constrainedModel);
-  } else {
-    constrainedModel.type = 'Object';
-  }
-  return constrainedModel; 
-}
-function constrainFloatModel(constrainedName: string, metaModel: FloatModel, typeMapping: TypeMapping): ConstrainedFloatModel {
-  const constrainedModel = new ConstrainedFloatModel(constrainedName, metaModel.originalInput, '');
-  if (typeMapping.Float !== undefined) {
-    constrainedModel.type = typeMapping.Float(constrainedModel);
-  } else {
+export const JavaDefaultTypeMapping: TypeMapping<JavaRenderer> = {
+  Object ({constrainedModel}): string {
+    return constrainedModel.name;
+  },
+  Reference ({constrainedModel}): string {
+    return constrainedModel.name;
+  },
+  Any (): string {
+    return 'Object';
+  },
+  Float ({constrainedModel}): string {
     let type = 'float';
-    const format = constrainedModel.originalInput['format'];
+    const format = constrainedModel.originalInput && constrainedModel.originalInput['format'];
     switch (format) {
-    case 'float':
-      type = 'float';
-      break;
     case 'double':
     case 'number':
       type = 'double';
       break;
     }
-    constrainedModel.type = type;
-  }
-  return constrainedModel;
-}
-function constrainIntegerModel(constrainedName: string, metaModel: IntegerModel, typeMapping: TypeMapping): ConstrainedIntegerModel {
-  const constrainedModel = new ConstrainedIntegerModel(constrainedName, metaModel.originalInput, '');
-  if (typeMapping.Integer !== undefined) {
-    constrainedModel.type = typeMapping.Integer(constrainedModel);
-  } else {
-    let type = 'integer';
-    const format = constrainedModel.originalInput['format'];
+    return type;
+  },
+  Integer ({constrainedModel}): string {
+    let type = 'Integer';
+    const format = constrainedModel.originalInput && constrainedModel.originalInput['format'];
     switch (format) {
     case 'integer':
     case 'int32':
@@ -91,26 +38,11 @@ function constrainIntegerModel(constrainedName: string, metaModel: IntegerModel,
       type = 'long';
       break;
     }
-    constrainedModel.type = type;
-  }
-  return constrainedModel;
-}
-function constrainStringModel(constrainedName: string, metaModel: IntegerModel, typeMapping: TypeMapping): ConstrainedIntegerModel {
-  const constrainedModel = new ConstrainedStringModel(constrainedName, metaModel.originalInput, '');
-  if (typeMapping.String !== undefined) {
-    constrainedModel.type = typeMapping.String(constrainedModel);
-  } else {
-    constrainedModel.type = 'string';
-  }
-  return constrainedModel;
-}
-function constrainBooleanModel(constrainedName: string, metaModel: BooleanModel, typeMapping: TypeMapping): ConstrainedBooleanModel {
-  const constrainedModel = new ConstrainedBooleanModel(constrainedName, metaModel.originalInput, '');
-  if (typeMapping.Boolean !== undefined) {
-    constrainedModel.type = typeMapping.Boolean(constrainedModel);
-  } else {
+    return type;
+  },
+  String ({constrainedModel}): string {
     let type = 'String';
-    const format = constrainedModel.originalInput['format'];
+    const format = constrainedModel.originalInput && constrainedModel.originalInput['format'];
     switch (format) {
     case 'date':
       type = 'java.time.LocalDate';
@@ -126,144 +58,40 @@ function constrainBooleanModel(constrainedName: string, metaModel: BooleanModel,
       type = 'byte[]';
       break;
     }
-    constrainedModel.type = type;
-  }
-  return constrainedModel;
-}
-function constrainTupleModel(constrainedName: string, metaModel: TupleModel, typeMapping: TypeMapping, constrainRules: JavaConstraints, options: JavaOptions): ConstrainedTupleModel {
-  const constrainedTupleModels = metaModel.tuple.map((tupleValue) => {
-    const tupleType = constrainMetaModel(tupleValue.value, typeMapping, constrainRules, options);
-    return new ConstrainedTupleValueModel(tupleValue.index, tupleType);
-  });
-  const constrainedModel = new ConstrainedTupleModel(constrainedName, metaModel.originalInput, '', constrainedTupleModels);
-  if (typeMapping.Tuple !== undefined) {
-    constrainedModel.type = typeMapping.Tuple(constrainedModel);
-  } else {
+    return type;
+  },
+  Boolean (): string {
+    return 'Boolean';
+  },
+  Tuple ({renderer}): string {
     //Because Java have no notion of tuples (and no custom implementation), we have to render it as a list of any value.
     const tupleType = 'Object';
-    if (options.collectionType && options.collectionType === 'List') {
-      constrainedModel.type = `List<${tupleType}>`;
-    } else {
-      constrainedModel.type = `${tupleType}[]`;
+    if (renderer.options.collectionType && renderer.options.collectionType === 'List') {
+      return `List<${tupleType}>`;
     }
-  }
-  return constrainedModel;
-}
-function constrainArrayModel(constrainedName: string, metaModel: ArrayModel, typeMapping: TypeMapping, constrainRules: JavaConstraints, options: JavaOptions): ConstrainedArrayModel {
-  const constrainedValueModel = constrainMetaModel(metaModel.valueModel, typeMapping, constrainRules, options);
-  const constrainedModel = new ConstrainedArrayModel(constrainedName, metaModel.originalInput, '', constrainedValueModel);
-  if (typeMapping.Array !== undefined) {
-    constrainedModel.type = typeMapping.Array(constrainedModel);
-  } else if (options.collectionType && options.collectionType === 'List') {
-    constrainedModel.type = `List<${constrainedValueModel.type}>`;
-  } else {
-    constrainedModel.type = `${constrainedValueModel.type}[]`;
-  }
-  return constrainedModel;
-}
-function constrainUnionModel(constrainedName: string, metaModel: UnionModel, typeMapping: TypeMapping, constrainRules: JavaConstraints, options: JavaOptions): ConstrainedUnionModel {
-  const constrainedUnionModels = metaModel.union.map((unionValue) => {
-    return constrainMetaModel(unionValue, typeMapping, constrainRules, options);
-  });
-  const constrainedModel = new ConstrainedUnionModel(constrainedName, metaModel.originalInput, '', constrainedUnionModels);
-  if (typeMapping.Union !== undefined) {
-    constrainedModel.type = typeMapping.Union(constrainedModel);
-  } else {
-    //Because Java have no notion of tuples (and no custom implementation), we have to render it as any value.
-    constrainedModel.type = 'Object';
-  }
-  return constrainedModel;
-}
-function constrainDictionaryModel(constrainedName: string, metaModel: DictionaryModel, typeMapping: TypeMapping, constrainRules: JavaConstraints, options: JavaOptions): ConstrainedDictionaryModel {
-  const keyModel = constrainMetaModel(metaModel.key, typeMapping, constrainRules, options);
-  const valueModel = constrainMetaModel(metaModel.value, typeMapping, constrainRules, options);
-  const constrainedModel = new ConstrainedDictionaryModel(constrainedName, metaModel.originalInput, '', keyModel, valueModel, metaModel.serializationType);
-  if (typeMapping.Dictionary !== undefined) {
-    constrainedModel.type = typeMapping.Dictionary(constrainedModel);
-  } else {
-    const type = `Map<${keyModel.type}, ${valueModel.type}>`;
-    constrainedModel.type = type;
-  }
-  return constrainedModel;
-}
-
-function constrainObjectModel(constrainedName: string, objectModel: ObjectModel, typeMapping: TypeMapping, constrainRules: JavaConstraints, options: JavaOptions): ConstrainedObjectModel {
-  const constrainedObjectModel = new ConstrainedObjectModel(constrainedName, objectModel.originalInput, '', {});
-  for (const [propertyKey, propertyMetaModel] of Object.entries(objectModel.properties)) {
-    const constrainedPropertyName = constrainRules.propertyKey({propertyKey, constrainedObjectModel, objectModel});
-    const constrainedProperty = constrainMetaModel(propertyMetaModel, typeMapping, constrainRules, options);
-    constrainedObjectModel.properties[String(constrainedPropertyName)] = constrainedProperty;
-  }
-  if (typeMapping.Object !== undefined) {
-    constrainedObjectModel.type = typeMapping.Object(constrainedObjectModel);
-  } else {
-    constrainedObjectModel.type = constrainedName;
-  }
-  return constrainedObjectModel;
-}
-
-export function ConstrainEnumModel(constrainedName: string, enumModel: EnumModel, typeMapping: TypeMapping, constrainRules: JavaConstraints): ConstrainedEnumModel {
-  const constrainedModel = new ConstrainedEnumModel(constrainedName, enumModel.originalInput, '', []);
-
-  for (const enumValue of enumModel.values) {
-    const constrainedEnumKey = constrainRules.enumKey({enumKey: String(enumValue.key), enumModel, constrainedEnumModel: constrainedModel});
-    let normalizedEnumValue;
-    switch (typeof enumValue.value) {
-    case 'string':
-    case 'boolean':
-      normalizedEnumValue = `"${enumValue.value}"`;
-      break;
-    case 'bigint':
-    case 'number': {
-      normalizedEnumValue = enumValue.value;
-      break;
-    }
-    case 'object': {
-      normalizedEnumValue = `"${JSON.stringify(enumValue.value).replace(/"/g, '\\"')}"`;
-      break;
+    return `${tupleType}[]`;
+  },
+  Array ({constrainedModel, renderer}): string {
+    if (renderer.options.collectionType && renderer.options.collectionType === 'List') {
+      return `List<${constrainedModel.valueModel.type}>`;
     }
-    default: {
-      normalizedEnumValue = String(enumValue.value);
-    }
-    }
-    const constrainedEnumValueModel = new ConstrainedEnumValueModel(constrainedEnumKey, normalizedEnumValue);
-    constrainedModel.values.push(constrainedEnumValueModel);
-  }
-  if (typeMapping.Enum !== undefined) {
-    constrainedModel.type = typeMapping.Enum(constrainedModel);
-  } else {
-    constrainedModel.type = constrainedName;
+    return `${constrainedModel.valueModel.type}[]`;
+  },
+  Enum ({constrainedModel}): string {
+    return constrainedModel.name;
+  },
+  Union (): string {
+    //Because Java have no notion of unions (and no custom implementation), we have to render it as any value.
+    return 'Object';
+  },
+  Dictionary ({constrainedModel}): string {
+    return `Map<${constrainedModel.key.type}, ${constrainedModel.value.type}>`;
   }
-  return constrainedModel;
-}
+};
 
-export function constrainMetaModel(metaModel: MetaModel, typeMapping: TypeMapping, constrainRules: JavaConstraints, options: JavaOptions): ConstrainedMetaModel {
-  const constrainedName = constrainRules.modelName({modelName: metaModel.name});
-  
-  if (metaModel instanceof ObjectModel) {
-    return constrainObjectModel(constrainedName, metaModel, typeMapping, constrainRules, options);
-  } else if (metaModel instanceof ReferenceModel) {
-    return constrainReferenceModel(constrainedName, metaModel, typeMapping, constrainRules, options);
-  } else if (metaModel instanceof AnyModel) {
-    return constrainAnyModel(constrainedName, metaModel, typeMapping);
-  } else if (metaModel instanceof FloatModel) {
-    return constrainFloatModel(constrainedName, metaModel, typeMapping);
-  } else if (metaModel instanceof IntegerModel) {
-    return constrainIntegerModel(constrainedName, metaModel, typeMapping);
-  } else if (metaModel instanceof StringModel) {
-    return constrainStringModel(constrainedName, metaModel, typeMapping);
-  } else if (metaModel instanceof BooleanModel) {
-    return constrainBooleanModel(constrainedName, metaModel, typeMapping);
-  } else if (metaModel instanceof TupleModel) {
-    return constrainTupleModel(constrainedName, metaModel, typeMapping, constrainRules, options);
-  } else if (metaModel instanceof ArrayModel) {
-    return constrainArrayModel(constrainedName, metaModel, typeMapping, constrainRules, options);
-  } else if (metaModel instanceof UnionModel) {
-    return constrainUnionModel(constrainedName, metaModel, typeMapping, constrainRules, options);
-  } else if (metaModel instanceof EnumModel) {
-    return ConstrainEnumModel(constrainedName, metaModel, typeMapping, constrainRules);
-  } else if (metaModel instanceof DictionaryModel) {
-    return constrainDictionaryModel(constrainedName, metaModel, typeMapping, constrainRules, options);
-  }
-  throw new Error('Could not constrain model');
-}
+export const JavaDefaultConstraints = {
+  enumKey: defaultEnumKeyConstraints(),
+  enumValue: defaultEnumValueConstraints(),
+  modelName: defaultModelNameConstraints(),
+  propertyKey: defaultPropertyKeyConstraints()
+};
diff --git a/src/generators/java/JavaGenerator.ts b/src/generators/java/JavaGenerator.ts
index 3ade5ad2..873c3b4e 100644
--- a/src/generators/java/JavaGenerator.ts
+++ b/src/generators/java/JavaGenerator.ts
@@ -4,15 +4,21 @@ import {
   defaultGeneratorOptions
 } from '../AbstractGenerator';
 import { CommonModel, CommonInputModel, RenderOutput } from '../../models';
-import { CommonNamingConvention, CommonNamingConventionImplementation, ModelKind, TypeHelpers } from '../../helpers';
+import { CommonNamingConvention, CommonNamingConventionImplementation, ModelKind, TypeHelpers, TypeMapping } from '../../helpers';
 import { JavaPreset, JAVA_DEFAULT_PRESET } from './JavaPreset';
 import { ClassRenderer } from './renderers/ClassRenderer';
 import { EnumRenderer } from './renderers/EnumRenderer';
 import { isReservedJavaKeyword } from './Constants';
 import { Logger } from '../../';
+import { JavaRenderer } from './JavaRenderer';
+import { Constraints } from '../../helpers/ConstrainHelpers';
+import { JavaDefaultConstraints, JavaDefaultTypeMapping } from './JavaConstrainer';
+
 export interface JavaOptions extends CommonGeneratorOptions<JavaPreset> {
-  collectionType?: 'List' | 'Array';
-  namingConvention?: CommonNamingConvention;
+  collectionType: 'List' | 'Array';
+  namingConvention: CommonNamingConvention;
+  typeMapping: TypeMapping<JavaRenderer>;
+  constraints: Constraints
 }
 export interface JavaRenderCompleteModelOptions {
   packageName: string
@@ -22,13 +28,17 @@ export class JavaGenerator extends AbstractGenerator<JavaOptions, JavaRenderComp
     ...defaultGeneratorOptions,
     defaultPreset: JAVA_DEFAULT_PRESET,     
     collectionType: 'Array',
-    namingConvention: CommonNamingConventionImplementation
+    namingConvention: CommonNamingConventionImplementation,
+    typeMapping: JavaDefaultTypeMapping,
+    constraints: JavaDefaultConstraints
   };
 
   constructor(
-    options: JavaOptions = JavaGenerator.defaultOptions,
+    options: Partial<JavaOptions> = JavaGenerator.defaultOptions,
   ) {
-    super('Java', JavaGenerator.defaultOptions, options);
+    const mergedOptions = {...JavaGenerator.defaultOptions, ...options};
+
+    super('Java', JavaGenerator.defaultOptions, mergedOptions);
   }
 
   /**
diff --git a/src/generators/java/constrainer/EnumConstrainer.ts b/src/generators/java/constrainer/EnumConstrainer.ts
index de988bd5..31bfd9d3 100644
--- a/src/generators/java/constrainer/EnumConstrainer.ts
+++ b/src/generators/java/constrainer/EnumConstrainer.ts
@@ -1,7 +1,7 @@
 /* eslint-disable @typescript-eslint/no-non-null-assertion */
 import { ConstrainedEnumModel, EnumModel } from '../../../models';
 import { NO_NUMBER_START_CHAR, NO_DUPLICATE_ENUM_KEYS, NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS} from '../../../helpers/Constraints';
-import { FormatHelpers } from '../../../helpers';
+import { FormatHelpers, EnumKeyConstraint, EnumValueConstraint} from '../../../helpers';
 import { isReservedJavaKeyword } from '../Constants';
 
 export type ModelEnumKeyConstraints = {
@@ -27,40 +27,42 @@ export const DefaultEnumKeyConstraints: ModelEnumKeyConstraints = {
   }
 };
 
-export type EnumKeyContext = {
-  enumKey: string,
-  constrainedEnumModel: ConstrainedEnumModel,
-  enumModel: EnumModel
+export function defaultEnumKeyConstraints(customConstraints?: Partial<ModelEnumKeyConstraints>): EnumKeyConstraint {
+  const constraints = {...DefaultEnumKeyConstraints, ...customConstraints};
+
+  return ({enumKey, enumModel, constrainedEnumModel}) => {
+    let constrainedEnumKey = enumKey;
+    constrainedEnumKey = constraints.NO_SPECIAL_CHAR(constrainedEnumKey);
+    constrainedEnumKey = constraints.NO_NUMBER_START_CHAR(constrainedEnumKey);
+    constrainedEnumKey = constraints.NO_EMPTY_VALUE(constrainedEnumKey);
+    constrainedEnumKey = constraints.NO_RESERVED_KEYWORDS(constrainedEnumKey);
+    constrainedEnumKey = constraints.NAMING_FORMATTER(constrainedEnumKey);
+    constrainedEnumKey = constraints.NO_DUPLICATE_KEYS(constrainedEnumModel, enumModel, constrainedEnumKey, constraints.NAMING_FORMATTER!);
+    return constrainedEnumKey;
+  };
 }
-export type EnumConstraintType = (context: EnumKeyContext, constraints?: ModelEnumKeyConstraints) => string;
 
-export function defaultEnumKeyConstraints(customConstraints?: ModelEnumKeyConstraints): EnumConstraintType {
-  const constraints = DefaultEnumKeyConstraints;
-  if (customConstraints !== undefined) {
-    if (customConstraints.NAMING_FORMATTER !== undefined) {
-      constraints.NAMING_FORMATTER = customConstraints.NAMING_FORMATTER;
+export function defaultEnumValueConstraints(): EnumValueConstraint {
+  return ({enumValue}) => {
+    let constrainedEnumValue = enumValue;
+    switch (typeof enumValue) {
+    case 'string':
+    case 'boolean':
+      constrainedEnumValue = `"${enumValue}"`;
+      break;
+    case 'bigint':
+    case 'number': {
+      constrainedEnumValue = enumValue;
+      break;
     }
-    if (customConstraints.NO_SPECIAL_CHAR !== undefined) {
-      constraints.NO_SPECIAL_CHAR = customConstraints.NO_SPECIAL_CHAR;
+    case 'object': {
+      constrainedEnumValue = `"${JSON.stringify(enumValue).replace(/"/g, '\\"')}"`;
+      break;
     }
-    if (customConstraints.NO_NUMBER_START_CHAR !== undefined) {
-      constraints.NO_NUMBER_START_CHAR = customConstraints.NO_NUMBER_START_CHAR;
+    default: {
+      constrainedEnumValue = `"${JSON.stringify(enumValue)}"`;
     }
-    if (customConstraints.NO_RESERVED_KEYWORDS !== undefined) {
-      constraints.NO_RESERVED_KEYWORDS = customConstraints.NO_RESERVED_KEYWORDS;
     }
-    if (customConstraints.NO_DUPLICATE_KEYS !== undefined) {
-      constraints.NO_DUPLICATE_KEYS = customConstraints.NO_DUPLICATE_KEYS;
-    }
-  }
-  return ({enumKey, enumModel, constrainedEnumModel}) => {
-    let constrainedEnumKey = enumKey;
-    constrainedEnumKey = constraints.NO_SPECIAL_CHAR!(constrainedEnumKey);
-    constrainedEnumKey = constraints.NO_NUMBER_START_CHAR!(constrainedEnumKey);
-    constrainedEnumKey = constraints.NO_EMPTY_VALUE!(constrainedEnumKey);
-    constrainedEnumKey = constraints.NO_RESERVED_KEYWORDS!(constrainedEnumKey);
-    constrainedEnumKey = constraints.NAMING_FORMATTER!(constrainedEnumKey);
-    constrainedEnumKey = constraints.NO_DUPLICATE_KEYS!(constrainedEnumModel, enumModel, constrainedEnumKey, constraints.NAMING_FORMATTER!);
-    return constrainedEnumKey;
+    return constrainedEnumValue;
   };
 }
diff --git a/src/generators/java/constrainer/ModelNameConstrainer.ts b/src/generators/java/constrainer/ModelNameConstrainer.ts
index 7da52a32..74ceb4d7 100644
--- a/src/generators/java/constrainer/ModelNameConstrainer.ts
+++ b/src/generators/java/constrainer/ModelNameConstrainer.ts
@@ -1,14 +1,13 @@
-/* eslint-disable @typescript-eslint/no-non-null-assertion */
 import { NO_NUMBER_START_CHAR, NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS} from '../../../helpers/Constraints';
-import { FormatHelpers } from '../../../helpers';
+import { FormatHelpers, ModelNameConstraint } from '../../../helpers';
 import { isReservedJavaKeyword } from '../Constants';
 
 export type ModelNameConstraints = {
-  NO_SPECIAL_CHAR?: (value: string) => string;
-  NO_NUMBER_START_CHAR?: (value: string) => string;
-  NO_EMPTY_VALUE?: (value: string) => string;
-  NAMING_FORMATTER?: (value: string) => string;
-  NO_RESERVED_KEYWORDS?: (value: string) => string;
+  NO_SPECIAL_CHAR: (value: string) => string;
+  NO_NUMBER_START_CHAR: (value: string) => string;
+  NO_EMPTY_VALUE: (value: string) => string;
+  NAMING_FORMATTER: (value: string) => string;
+  NO_RESERVED_KEYWORDS: (value: string) => string;
 };
 
 export const DefaultModelNameConstraints: ModelNameConstraints = {
@@ -26,38 +25,16 @@ export const DefaultModelNameConstraints: ModelNameConstraints = {
     return NO_RESERVED_KEYWORDS(value, isReservedJavaKeyword); 
   }
 };
-export type ModelNameContext = {
-  modelName: string
-}
-export type ModelNameConstraintType = (context: ModelNameContext) => string;
-
-export function defaultModelNameConstraints(customConstraints?: ModelNameConstraints): ModelNameConstraintType {
-  const constraints = DefaultModelNameConstraints;
-  if (customConstraints !== undefined) {
-    if (customConstraints.NO_SPECIAL_CHAR !== undefined) {
-      constraints.NO_SPECIAL_CHAR = customConstraints.NO_SPECIAL_CHAR;
-    }
-    if (customConstraints.NO_NUMBER_START_CHAR !== undefined) {
-      constraints.NO_NUMBER_START_CHAR = customConstraints.NO_NUMBER_START_CHAR;
-    }
-    if (customConstraints.NO_EMPTY_VALUE !== undefined) {
-      constraints.NO_EMPTY_VALUE = customConstraints.NO_EMPTY_VALUE;
-    }
-    if (customConstraints.NAMING_FORMATTER !== undefined) {
-      constraints.NAMING_FORMATTER = customConstraints.NAMING_FORMATTER;
-    }
-    if (customConstraints.NO_RESERVED_KEYWORDS !== undefined) {
-      constraints.NAMING_FORMATTER = customConstraints.NO_RESERVED_KEYWORDS;
-    }
-  }
+export function defaultModelNameConstraints(customConstraints?: Partial<ModelNameConstraints>): ModelNameConstraint {
+  const constraints = {...DefaultModelNameConstraints, ...customConstraints};
 
   return ({modelName}) => {
     let constrainedValue = modelName;
-    constrainedValue = constraints.NO_SPECIAL_CHAR!(constrainedValue);
-    constrainedValue = constraints.NO_NUMBER_START_CHAR!(constrainedValue);
-    constrainedValue = constraints.NO_EMPTY_VALUE!(constrainedValue);
-    constrainedValue = constraints.NAMING_FORMATTER!(constrainedValue);
-    constrainedValue = constraints.NO_RESERVED_KEYWORDS!(constrainedValue);
+    constrainedValue = constraints.NO_SPECIAL_CHAR(constrainedValue);
+    constrainedValue = constraints.NO_NUMBER_START_CHAR(constrainedValue);
+    constrainedValue = constraints.NO_EMPTY_VALUE(constrainedValue);
+    constrainedValue = constraints.NO_RESERVED_KEYWORDS(constrainedValue);
+    constrainedValue = constraints.NAMING_FORMATTER(constrainedValue);
     return constrainedValue;
   };
 }
diff --git a/src/generators/java/constrainer/PropertyKeyConstrainer.ts b/src/generators/java/constrainer/PropertyKeyConstrainer.ts
index 21d5af07..fd1cd24e 100644
--- a/src/generators/java/constrainer/PropertyKeyConstrainer.ts
+++ b/src/generators/java/constrainer/PropertyKeyConstrainer.ts
@@ -1,16 +1,15 @@
-/* eslint-disable @typescript-eslint/no-non-null-assertion */
 import { ConstrainedObjectModel, ObjectModel } from '../../../models';
 import { NO_NUMBER_START_CHAR, NO_DUPLICATE_PROPERTIES, NO_EMPTY_VALUE, NO_RESERVED_KEYWORDS} from '../../../helpers/Constraints';
-import { FormatHelpers } from '../../../helpers';
+import { FormatHelpers, PropertyKeyConstraint } from '../../../helpers';
 import { isReservedJavaKeyword } from '../Constants';
 
 export type PropertyKeyConstraintOptions = {
-  NO_SPECIAL_CHAR?: (value: string) => string;
-  NO_NUMBER_START_CHAR?: (value: string) => string;
-  NO_DUPLICATE_PROPERTIES?: (constrainedObjectModel: ConstrainedObjectModel, objectModel: ObjectModel, propertyName: string, namingFormatter: (value: string) => string) => string;
-  NO_EMPTY_VALUE?: (value: string) => string;
-  NAMING_FORMATTER?: (value: string) => string;
-  NO_RESERVED_KEYWORDS?: (value: string) => string;
+  NO_SPECIAL_CHAR: (value: string) => string;
+  NO_NUMBER_START_CHAR: (value: string) => string;
+  NO_DUPLICATE_PROPERTIES: (constrainedObjectModel: ConstrainedObjectModel, objectModel: ObjectModel, propertyName: string, namingFormatter: (value: string) => string) => string;
+  NO_EMPTY_VALUE: (value: string) => string;
+  NAMING_FORMATTER: (value: string) => string;
+  NO_RESERVED_KEYWORDS: (value: string) => string;
 };
 
 export const DefaultPropertyKeyConstraints: PropertyKeyConstraintOptions = {
@@ -28,45 +27,18 @@ export const DefaultPropertyKeyConstraints: PropertyKeyConstraintOptions = {
   }
 };
 
-export type PropertyKeyContext = {
-  propertyKey: string,
-  constrainedObjectModel: ConstrainedObjectModel,
-  objectModel: ObjectModel
-}
-
-export type PropertyKeyConstraintType = (context: PropertyKeyContext) => string;
+export function defaultPropertyKeyConstraints(customConstraints?: Partial<PropertyKeyConstraintOptions>): PropertyKeyConstraint {
+  const constraints = {...DefaultPropertyKeyConstraints, ...customConstraints};
 
-export function defaultPropertyKeyConstraints(customConstraints?: PropertyKeyConstraintOptions): PropertyKeyConstraintType {
-  const constraints = DefaultPropertyKeyConstraints;
-  if (customConstraints !== undefined) {
-    if (customConstraints.NAMING_FORMATTER !== undefined) {
-      constraints.NAMING_FORMATTER = customConstraints.NAMING_FORMATTER;
-    }
-    if (customConstraints.NO_SPECIAL_CHAR !== undefined) {
-      constraints.NO_SPECIAL_CHAR = customConstraints.NO_SPECIAL_CHAR;
-    }
-    if (customConstraints.NO_NUMBER_START_CHAR !== undefined) {
-      constraints.NO_NUMBER_START_CHAR = customConstraints.NO_NUMBER_START_CHAR;
-    }
-    if (customConstraints.NO_EMPTY_VALUE !== undefined) {
-      constraints.NO_EMPTY_VALUE = customConstraints.NO_EMPTY_VALUE;
-    }
-    if (customConstraints.NO_RESERVED_KEYWORDS !== undefined) {
-      constraints.NO_RESERVED_KEYWORDS = customConstraints.NO_RESERVED_KEYWORDS;
-    }
-    if (customConstraints.NO_DUPLICATE_PROPERTIES !== undefined) {
-      constraints.NO_DUPLICATE_PROPERTIES = customConstraints.NO_DUPLICATE_PROPERTIES;
-    }
-  }
-  return ({propertyKey, constrainedObjectModel, objectModel}: PropertyKeyContext) => {
+  return ({propertyKey, constrainedObjectModel, objectModel}) => {
     let constrainedPropertyKey = propertyKey;
 
-    constrainedPropertyKey = constraints.NO_SPECIAL_CHAR!(constrainedPropertyKey);
-    constrainedPropertyKey = constraints.NO_NUMBER_START_CHAR!(constrainedPropertyKey);
-    constrainedPropertyKey = constraints.NO_EMPTY_VALUE!(constrainedPropertyKey);
-    constrainedPropertyKey = constraints.NO_RESERVED_KEYWORDS!(constrainedPropertyKey);
-    constrainedPropertyKey = constraints.NAMING_FORMATTER!(constrainedPropertyKey);
-    constrainedPropertyKey = constraints.NO_DUPLICATE_PROPERTIES!(constrainedObjectModel, objectModel, constrainedPropertyKey, constraints.NAMING_FORMATTER!);
+    constrainedPropertyKey = constraints.NO_SPECIAL_CHAR(constrainedPropertyKey);
+    constrainedPropertyKey = constraints.NO_NUMBER_START_CHAR(constrainedPropertyKey);
+    constrainedPropertyKey = constraints.NO_EMPTY_VALUE(constrainedPropertyKey);
+    constrainedPropertyKey = constraints.NO_RESERVED_KEYWORDS(constrainedPropertyKey);
+    constrainedPropertyKey = constraints.NAMING_FORMATTER(constrainedPropertyKey);
+    constrainedPropertyKey = constraints.NO_DUPLICATE_PROPERTIES(constrainedObjectModel, objectModel, constrainedPropertyKey, constraints.NAMING_FORMATTER);
     return constrainedPropertyKey;
   };
 }
diff --git a/src/helpers/ConstrainHelpers.ts b/src/helpers/ConstrainHelpers.ts
new file mode 100644
index 00000000..a456ab39
--- /dev/null
+++ b/src/helpers/ConstrainHelpers.ts
@@ -0,0 +1,207 @@
+import { AbstractRenderer } from '../generators';
+import { ConstrainedAnyModel, ConstrainedBooleanModel, ConstrainedFloatModel, ConstrainedIntegerModel, ConstrainedMetaModel, ConstrainedObjectModel, ConstrainedReferenceModel, ConstrainedStringModel, ConstrainedTupleModel, ConstrainedTupleValueModel, ConstrainedArrayModel, ConstrainedUnionModel, ConstrainedEnumModel, ConstrainedDictionaryModel, ConstrainedEnumValueModel } from '../models/ConstrainedMetaModel';
+import { AnyModel, BooleanModel, FloatModel, IntegerModel, ObjectModel, ReferenceModel, StringModel, TupleModel, ArrayModel, UnionModel, EnumModel, DictionaryModel, MetaModel } from '../models/MetaModel';
+import { getTypeFromMapping, TypeMapping } from './TypeHelpers';
+
+export type ConstrainContext<R extends AbstractRenderer, M extends MetaModel> = {
+  propertyKey?: string,
+  metaModel: M,
+  constrainedName: string,
+  renderer: R
+}
+
+export type EnumKeyContext = {
+  enumKey: string,
+  constrainedEnumModel: ConstrainedEnumModel,
+  enumModel: EnumModel
+}
+export type EnumKeyConstraint = (context: EnumKeyContext) => string;
+
+export type EnumValueContext = {
+  enumValue: any,
+  constrainedEnumModel: ConstrainedEnumModel,
+  enumModel: EnumModel
+}
+export type EnumValueConstraint = (context: EnumValueContext) => string;
+
+export type ModelNameContext = {
+  modelName: string
+}
+export type ModelNameConstraint = (context: ModelNameContext) => string;
+
+export type PropertyKeyContext = {
+  propertyKey: string,
+  constrainedObjectModel: ConstrainedObjectModel,
+  objectModel: ObjectModel
+}
+
+export type PropertyKeyConstraint = (context: PropertyKeyContext) => string;
+
+export interface Constraints {
+  enumKey: EnumKeyConstraint,
+  enumValue: EnumValueConstraint,
+  modelName: ModelNameConstraint,
+  propertyKey: PropertyKeyConstraint,
+}
+
+function constrainReferenceModel<R extends AbstractRenderer>(typeMapping: TypeMapping<R>, constrainRules: Constraints, context: ConstrainContext<R, ReferenceModel>): ConstrainedReferenceModel {
+  const constrainedRefModel = constrainMetaModel(typeMapping, constrainRules, {...context, metaModel: context.metaModel.ref, propertyKey: undefined});
+  const constrainedModel = new ConstrainedReferenceModel(context.constrainedName, context.metaModel.originalInput, '', constrainedRefModel);
+  constrainedModel.type = getTypeFromMapping(typeMapping, {
+    constrainedModel,
+    renderer: context.renderer,
+    propertyKey: context.propertyKey
+  });
+  return constrainedModel;
+}
+function constrainAnyModel<R extends AbstractRenderer>(typeMapping: TypeMapping<R>, context: ConstrainContext<R, AnyModel>): ConstrainedAnyModel {
+  const constrainedModel = new ConstrainedAnyModel(context.constrainedName, context.metaModel.originalInput, '');
+  constrainedModel.type = getTypeFromMapping(typeMapping, {
+    constrainedModel,
+    renderer: context.renderer,
+    propertyKey: context.propertyKey
+  });
+  return constrainedModel; 
+}
+function constrainFloatModel<R extends AbstractRenderer>(typeMapping: TypeMapping<R>, context: ConstrainContext<R, FloatModel>): ConstrainedFloatModel {
+  const constrainedModel = new ConstrainedFloatModel(context.constrainedName, context.metaModel.originalInput, '');
+  constrainedModel.type = getTypeFromMapping(typeMapping, {
+    constrainedModel,
+    renderer: context.renderer,
+    propertyKey: context.propertyKey
+  });
+  return constrainedModel;
+}
+function constrainIntegerModel<R extends AbstractRenderer>(typeMapping: TypeMapping<R>, context: ConstrainContext<R, IntegerModel>): ConstrainedIntegerModel {
+  const constrainedModel = new ConstrainedIntegerModel(context.constrainedName, context.metaModel.originalInput, '');
+  constrainedModel.type = getTypeFromMapping(typeMapping, {
+    constrainedModel,
+    renderer: context.renderer,
+    propertyKey: context.propertyKey
+  });
+  return constrainedModel;
+}
+function constrainStringModel<R extends AbstractRenderer>(typeMapping: TypeMapping<R>, context: ConstrainContext<R, StringModel>): ConstrainedStringModel {
+  const constrainedModel = new ConstrainedStringModel(context.constrainedName, context.metaModel.originalInput, '');
+  constrainedModel.type = getTypeFromMapping(typeMapping, {
+    constrainedModel,
+    renderer: context.renderer,
+    propertyKey: context.propertyKey
+  });
+  return constrainedModel;
+}
+function constrainBooleanModel<R extends AbstractRenderer>(typeMapping: TypeMapping<R>, context: ConstrainContext<R, BooleanModel>): ConstrainedBooleanModel {
+  const constrainedModel = new ConstrainedBooleanModel(context.constrainedName, context.metaModel.originalInput, '');
+  constrainedModel.type = getTypeFromMapping(typeMapping, {
+    constrainedModel,
+    renderer: context.renderer,
+    propertyKey: context.propertyKey
+  });
+  return constrainedModel;
+}
+function constrainTupleModel<R extends AbstractRenderer>(typeMapping: TypeMapping<R>, constrainRules: Constraints, context: ConstrainContext<R, TupleModel>): ConstrainedTupleModel {
+  const constrainedTupleModels = context.metaModel.tuple.map((tupleValue) => {
+    const tupleType = constrainMetaModel(typeMapping, constrainRules, {...context, metaModel: tupleValue.value, propertyKey: undefined});
+    return new ConstrainedTupleValueModel(tupleValue.index, tupleType);
+  });
+  const constrainedModel = new ConstrainedTupleModel(context.constrainedName, context.metaModel.originalInput, '', constrainedTupleModels);
+  constrainedModel.type = getTypeFromMapping(typeMapping, {
+    constrainedModel,
+    renderer: context.renderer,
+    propertyKey: context.propertyKey
+  });
+  return constrainedModel;
+}
+function constrainArrayModel<R extends AbstractRenderer>(typeMapping: TypeMapping<R>, constrainRules: Constraints, context: ConstrainContext<R, ArrayModel>): ConstrainedArrayModel {
+  const constrainedValueModel = constrainMetaModel(typeMapping, constrainRules, {...context, metaModel: context.metaModel.valueModel, propertyKey: undefined});
+  const constrainedModel = new ConstrainedArrayModel(context.constrainedName, context.metaModel.originalInput, '', constrainedValueModel);
+  constrainedModel.type = getTypeFromMapping(typeMapping, {
+    constrainedModel,
+    renderer: context.renderer,
+    propertyKey: context.propertyKey
+  });
+  return constrainedModel;
+}
+function constrainUnionModel<R extends AbstractRenderer>(typeMapping: TypeMapping<R>, constrainRules: Constraints, context: ConstrainContext<R, UnionModel>): ConstrainedUnionModel {
+  const constrainedUnionModels = context.metaModel.union.map((unionValue) => {
+    return constrainMetaModel(typeMapping, constrainRules, {...context, metaModel: unionValue, propertyKey: undefined});
+  });
+  const constrainedModel = new ConstrainedUnionModel(context.constrainedName, context.metaModel.originalInput, '', constrainedUnionModels);
+  constrainedModel.type = getTypeFromMapping(typeMapping, {
+    constrainedModel,
+    renderer: context.renderer,
+    propertyKey: context.propertyKey
+  });
+  return constrainedModel;
+}
+function constrainDictionaryModel<R extends AbstractRenderer>(typeMapping: TypeMapping<R>, constrainRules: Constraints, context: ConstrainContext<R, DictionaryModel>): ConstrainedDictionaryModel {
+  const keyModel = constrainMetaModel(typeMapping, constrainRules, {...context, metaModel: context.metaModel.key, propertyKey: undefined});
+  const valueModel = constrainMetaModel(typeMapping, constrainRules, {...context, metaModel: context.metaModel.value, propertyKey: undefined});
+  const constrainedModel = new ConstrainedDictionaryModel(context.constrainedName, context.metaModel.originalInput, '', keyModel, valueModel, context.metaModel.serializationType);
+  constrainedModel.type = getTypeFromMapping(typeMapping, {
+    constrainedModel,
+    renderer: context.renderer,
+    propertyKey: context.propertyKey
+  });
+  return constrainedModel;
+}
+
+function constrainObjectModel<R extends AbstractRenderer>(typeMapping: TypeMapping<R>, constrainRules: Constraints, context: ConstrainContext<R, ObjectModel>): ConstrainedObjectModel {
+  const constrainedModel = new ConstrainedObjectModel(context.constrainedName, context.metaModel.originalInput, '', {});
+  for (const [propertyKey, propertyMetaModel] of Object.entries(context.metaModel.properties)) {
+    const constrainedPropertyName = constrainRules.propertyKey({propertyKey, constrainedObjectModel: constrainedModel, objectModel: context.metaModel});
+    const constrainedProperty = constrainMetaModel(typeMapping, constrainRules, {...context, metaModel: propertyMetaModel, propertyKey: constrainedPropertyName});
+    constrainedModel.properties[String(constrainedPropertyName)] = constrainedProperty;
+  }
+  constrainedModel.type = getTypeFromMapping(typeMapping, {
+    constrainedModel,
+    renderer: context.renderer,
+    propertyKey: context.propertyKey
+  });
+  return constrainedModel;
+}
+
+function ConstrainEnumModel<R extends AbstractRenderer>(typeMapping: TypeMapping<R>, constrainRules: Constraints, context: ConstrainContext<R, EnumModel>): ConstrainedEnumModel {
+  const constrainedModel = new ConstrainedEnumModel(context.constrainedName, context.metaModel.originalInput, '', []);
+
+  for (const enumValue of context.metaModel.values) {
+    const constrainedEnumKey = constrainRules.enumKey({enumKey: String(enumValue.key), enumModel: context.metaModel, constrainedEnumModel: constrainedModel});
+    const constrainedEnumValue = constrainRules.enumKey({enumKey: String(enumValue.key), enumModel: context.metaModel, constrainedEnumModel: constrainedModel});
+
+    const constrainedEnumValueModel = new ConstrainedEnumValueModel(constrainedEnumKey, constrainedEnumValue);
+    constrainedModel.values.push(constrainedEnumValueModel);
+  }
+  constrainedModel.type = getTypeFromMapping(typeMapping, {constrainedModel, renderer: context.renderer, propertyKey: context.propertyKey});
+  return constrainedModel;
+}
+
+export function constrainMetaModel<R extends AbstractRenderer>(typeMapping: TypeMapping<R>, constrainRules: Constraints, context: ConstrainContext<R, MetaModel>): ConstrainedMetaModel {
+  const constrainedName = constrainRules.modelName({modelName: context.metaModel.name});
+  const newContext = {...context, constrainedName};
+  if (newContext.metaModel instanceof ObjectModel) {
+    return constrainObjectModel(typeMapping, constrainRules, {...newContext, metaModel: newContext.metaModel});
+  } else if (newContext.metaModel instanceof ReferenceModel) {
+    return constrainReferenceModel(typeMapping, constrainRules, {...newContext, metaModel: newContext.metaModel});
+  } else if (newContext.metaModel instanceof AnyModel) {
+    return constrainAnyModel(typeMapping, {...newContext, metaModel: newContext.metaModel});
+  } else if (newContext.metaModel instanceof FloatModel) {
+    return constrainFloatModel(typeMapping, {...newContext, metaModel: newContext.metaModel});
+  } else if (newContext.metaModel instanceof IntegerModel) {
+    return constrainIntegerModel(typeMapping, {...newContext, metaModel: newContext.metaModel});
+  } else if (newContext.metaModel instanceof StringModel) {
+    return constrainStringModel(typeMapping, {...newContext, metaModel: newContext.metaModel});
+  } else if (newContext.metaModel instanceof BooleanModel) {
+    return constrainBooleanModel(typeMapping, {...newContext, metaModel: newContext.metaModel});
+  } else if (newContext.metaModel instanceof TupleModel) {
+    return constrainTupleModel(typeMapping, constrainRules, {...newContext, metaModel: newContext.metaModel});
+  } else if (newContext.metaModel instanceof ArrayModel) {
+    return constrainArrayModel(typeMapping, constrainRules, {...newContext, metaModel: newContext.metaModel});
+  } else if (newContext.metaModel instanceof UnionModel) {
+    return constrainUnionModel(typeMapping, constrainRules, {...newContext, metaModel: newContext.metaModel});
+  } else if (newContext.metaModel instanceof EnumModel) {
+    return ConstrainEnumModel(typeMapping, constrainRules, {...newContext, metaModel: newContext.metaModel});
+  } else if (newContext.metaModel instanceof DictionaryModel) {
+    return constrainDictionaryModel(typeMapping, constrainRules, {...newContext, metaModel: newContext.metaModel});
+  }
+  throw new Error('Could not constrain model');
+}
diff --git a/src/helpers/Constraints.ts b/src/helpers/Constraints.ts
index c5c3b34e..53e05932 100644
--- a/src/helpers/Constraints.ts
+++ b/src/helpers/Constraints.ts
@@ -3,7 +3,7 @@ import { ObjectModel, EnumModel } from '../models/MetaModel';
 
 export function NO_NUMBER_START_CHAR(value: string): string {
   const firstChar = value.charAt(0);
-  if (isNaN(+firstChar)) {
+  if (firstChar !== '' && !isNaN(+firstChar)) {
     return `number_${value}`;
   }
   return value;
@@ -28,12 +28,12 @@ export function NO_DUPLICATE_PROPERTIES(constrainedObjectModel: ConstrainedObjec
 export function NO_DUPLICATE_ENUM_KEYS(constrainedEnumModel: ConstrainedEnumModel, enumModel: EnumModel, enumKey: string, namingFormatter: (value: string) => string): string {
   let newEnumKey = enumKey;
 
-  const alreadyPartOfMetaModel = constrainedEnumModel.values.map((model) => model.key).map((key) => namingFormatter(key)).includes(enumKey);
+  const alreadyPartOfMetaModel = enumModel.values.map((model) => model.key).map((key) => namingFormatter(key)).includes(enumKey);
   const alreadyPartOfConstrainedModel = constrainedEnumModel.values.map((model) => model.key).includes(enumKey);
 
   if (alreadyPartOfMetaModel || alreadyPartOfConstrainedModel) {
     newEnumKey = `reserved_${enumKey}`;
-    newEnumKey = namingFormatter(enumKey);
+    newEnumKey = namingFormatter(newEnumKey);
     newEnumKey = NO_DUPLICATE_ENUM_KEYS(constrainedEnumModel, enumModel, newEnumKey, namingFormatter);
   }
   return newEnumKey;
diff --git a/src/helpers/TypeHelpers.ts b/src/helpers/TypeHelpers.ts
index fd49970f..1783c1ef 100644
--- a/src/helpers/TypeHelpers.ts
+++ b/src/helpers/TypeHelpers.ts
@@ -1,5 +1,7 @@
 /* eslint-disable no-unused-vars */
+import { AbstractRenderer } from '../generators';
 import { CommonModel } from '../models';
+import { ConstrainedAnyModel, ConstrainedBooleanModel, ConstrainedFloatModel, ConstrainedIntegerModel, ConstrainedMetaModel, ConstrainedObjectModel, ConstrainedReferenceModel, ConstrainedStringModel, ConstrainedTupleModel, ConstrainedArrayModel, ConstrainedUnionModel, ConstrainedEnumModel, ConstrainedDictionaryModel } from '../models/ConstrainedMetaModel';
 
 export enum ModelKind {
   OBJECT = 'object',
@@ -23,3 +25,55 @@ export class TypeHelpers {
     return ModelKind.PRIMITIVE;
   }
 }
+
+export type TypeContext<T extends ConstrainedMetaModel, R extends AbstractRenderer> = {
+  propertyKey?: string,
+  renderer: R,
+  constrainedModel: T,
+}
+
+export type TypeMappingFunction<T extends ConstrainedMetaModel, R extends AbstractRenderer> = (context: TypeContext<T, R>) => string;
+
+export type TypeMapping<R extends AbstractRenderer> = {
+  Object: TypeMappingFunction<ConstrainedObjectModel, R>,
+  Reference: TypeMappingFunction<ConstrainedReferenceModel, R>,
+  Any: TypeMappingFunction<ConstrainedAnyModel, R>,
+  Float: TypeMappingFunction<ConstrainedFloatModel, R>,
+  Integer: TypeMappingFunction<ConstrainedIntegerModel, R>,
+  String: TypeMappingFunction<ConstrainedStringModel, R>,
+  Boolean: TypeMappingFunction<ConstrainedBooleanModel, R>,
+  Tuple: TypeMappingFunction<ConstrainedTupleModel, R>,
+  Array: TypeMappingFunction<ConstrainedArrayModel, R>,
+  Enum: TypeMappingFunction<ConstrainedEnumModel, R>,
+  Union: TypeMappingFunction<ConstrainedUnionModel, R>,
+  Dictionary: TypeMappingFunction<ConstrainedDictionaryModel, R>
+};
+
+export function getTypeFromMapping<T extends ConstrainedMetaModel, R extends AbstractRenderer>(typeMapping: TypeMapping<R>, context: TypeContext<T, R>): string {
+  if (context.constrainedModel instanceof ConstrainedObjectModel) {
+    return typeMapping.Object({...context, constrainedModel: context.constrainedModel});
+  } else if (context.constrainedModel instanceof ConstrainedReferenceModel) {
+    return typeMapping.Reference({...context, constrainedModel: context.constrainedModel});
+  } else if (context.constrainedModel instanceof ConstrainedAnyModel) {
+    return typeMapping.Any(context);
+  } else if (context.constrainedModel instanceof ConstrainedFloatModel) {
+    return typeMapping.Float(context);
+  } else if (context.constrainedModel instanceof ConstrainedIntegerModel) {
+    return typeMapping.Integer(context);
+  } else if (context.constrainedModel instanceof ConstrainedStringModel) {
+    return typeMapping.String(context);
+  } else if (context.constrainedModel instanceof ConstrainedBooleanModel) {
+    return typeMapping.Boolean(context);
+  } else if (context.constrainedModel instanceof ConstrainedTupleModel) {
+    return typeMapping.Tuple({...context, constrainedModel: context.constrainedModel});
+  } else if (context.constrainedModel instanceof ConstrainedArrayModel) {
+    return typeMapping.Array({...context, constrainedModel: context.constrainedModel});
+  } else if (context.constrainedModel instanceof ConstrainedEnumModel) {
+    return typeMapping.Enum({...context, constrainedModel: context.constrainedModel});
+  } else if (context.constrainedModel instanceof ConstrainedUnionModel) {
+    return typeMapping.Union({...context, constrainedModel: context.constrainedModel});
+  } else if (context.constrainedModel instanceof ConstrainedDictionaryModel) {
+    return typeMapping.Dictionary({...context, constrainedModel: context.constrainedModel});
+  }
+  throw new Error('Could not find type for model');
+}
diff --git a/src/helpers/index.ts b/src/helpers/index.ts
index c5f8de00..6770022a 100644
--- a/src/helpers/index.ts
+++ b/src/helpers/index.ts
@@ -4,3 +4,5 @@ export * from './NameHelpers';
 export * from './FileHelpers';
 export * from './CommonModelToMetaModel';
 export * from './Splitter';
+export * from './Constraints';
+export * from './ConstrainHelpers';


Test patch:
diff --git a/test/TestUtils/TestGenerator.ts b/test/TestUtils/TestGenerator.ts
new file mode 100644
index 00000000..8feb1131
--- /dev/null
+++ b/test/TestUtils/TestGenerator.ts
@@ -0,0 +1,22 @@
+/* eslint-disable @typescript-eslint/no-unused-vars */
+import { AbstractGenerator, CommonInputModel, CommonModel, IndentationTypes, RenderOutput } from '../../src';
+
+export const testOptions = {
+  indentation: {
+    type: IndentationTypes.SPACES,
+    size: 2,
+  }
+};
+export class TestGenerator extends AbstractGenerator {
+  constructor() {
+    super('TestGenerator', testOptions);
+  }
+
+  render(model: CommonModel, _inputModel: CommonInputModel): Promise<RenderOutput> {
+    return Promise.resolve(RenderOutput.toRenderOutput({result: model.$id || 'rendered content', renderedName: 'TestName'}));
+  }
+
+  renderCompleteModel(_model: CommonModel, _inputModel: CommonInputModel, _options: any): Promise<RenderOutput> {
+    return Promise.resolve(RenderOutput.toRenderOutput({result: 'rendered complete content', renderedName: 'TestName'}));
+  }
+}
diff --git a/test/TestUtils/TestRenderers.ts b/test/TestUtils/TestRenderers.ts
new file mode 100644
index 00000000..311087cf
--- /dev/null
+++ b/test/TestUtils/TestRenderers.ts
@@ -0,0 +1,16 @@
+import { AbstractRenderer, CommonInputModel, CommonModel, RenderOutput } from '../../src';
+import { JavaRenderer } from '../../src/generators/java/JavaRenderer';
+import {testOptions, TestGenerator} from './TestGenerator';
+
+export class TestRenderer extends AbstractRenderer {
+  constructor(presets = []) {
+    super(testOptions, new TestGenerator(), presets, new CommonModel(), new CommonInputModel());
+  }
+  render(): Promise<RenderOutput> {
+    return Promise.resolve(RenderOutput.toRenderOutput({result: '', renderedName: ''}));
+  }
+}
+
+export class MockJavaRenderer extends JavaRenderer {
+
+}
diff --git a/test/generators/AbstractGenerator.spec.ts b/test/generators/AbstractGenerator.spec.ts
index 9aa97c3f..df601c65 100644
--- a/test/generators/AbstractGenerator.spec.ts
+++ b/test/generators/AbstractGenerator.spec.ts
@@ -1,26 +1,6 @@
-import { AbstractGenerator, FileGenerator } from '../../src/generators'; 
-import { IndentationTypes } from '../../src/helpers';
-import { CommonInputModel, CommonModel, OutputModel, RenderOutput } from '../../src/models';
-
-export const testOptions = {
-  indentation: {
-    type: IndentationTypes.SPACES,
-    size: 2,
-  }
-};
-export class TestGenerator extends AbstractGenerator {
-  constructor() {
-    super('TestGenerator', testOptions);
-  }
-
-  render(model: CommonModel, inputModel: CommonInputModel): Promise<RenderOutput> {
-    return Promise.resolve(RenderOutput.toRenderOutput({result: model.$id || 'rendered content', renderedName: 'TestName'}));
-  }
-
-  renderCompleteModel(model: CommonModel, inputModel: CommonInputModel, options: any): Promise<RenderOutput> {
-    return Promise.resolve(RenderOutput.toRenderOutput({result: 'rendered complete content', renderedName: 'TestName'}));
-  }
-}
+import { AbstractGenerator } from '../../src/generators'; 
+import { CommonInputModel, CommonModel, RenderOutput } from '../../src/models';
+import {TestGenerator} from '../TestUtils/TestGenerator';
 describe('AbstractGenerator', () => {
   let generator: TestGenerator;
   beforeEach(() => {
diff --git a/test/generators/AbstractRenderer.spec.ts b/test/generators/AbstractRenderer.spec.ts
index 07478336..8e8d0d57 100644
--- a/test/generators/AbstractRenderer.spec.ts
+++ b/test/generators/AbstractRenderer.spec.ts
@@ -1,18 +1,7 @@
-import { AbstractRenderer } from '../../src/generators'; 
 import { IndentationTypes } from '../../src/helpers';
-import { CommonInputModel, CommonModel, RenderOutput } from '../../src/models';
-import { testOptions, TestGenerator } from './AbstractGenerator.spec';
+import {TestRenderer} from '../TestUtils/TestRenderers';
 
 describe('AbstractRenderer', () => {
-  class TestRenderer extends AbstractRenderer {
-    constructor(presets = []) {
-      super(testOptions, new TestGenerator(), presets, new CommonModel(), new CommonInputModel());
-    }
-    render(): Promise<RenderOutput> {
-      return Promise.resolve(RenderOutput.toRenderOutput({result: '', renderedName: ''}));
-    }
-  }
-
   let renderer: TestRenderer;
   beforeEach(() => {
     renderer = new TestRenderer();
diff --git a/test/generators/java/JavaConstrainer.spec.ts b/test/generators/java/JavaConstrainer.spec.ts
new file mode 100644
index 00000000..ff34c67b
--- /dev/null
+++ b/test/generators/java/JavaConstrainer.spec.ts
@@ -0,0 +1,172 @@
+import {JavaDefaultTypeMapping } from '../../../src/generators/java/JavaConstrainer';
+import {MockJavaRenderer} from '../../TestUtils/TestRenderers';
+import { JavaRenderer } from '../../../src/generators/java/JavaRenderer';
+import { CommonInputModel, CommonModel, ConstrainedAnyModel, ConstrainedArrayModel, ConstrainedBooleanModel, ConstrainedDictionaryModel, ConstrainedEnumModel, ConstrainedFloatModel, ConstrainedIntegerModel, ConstrainedObjectModel, ConstrainedReferenceModel, ConstrainedStringModel, ConstrainedTupleModel, ConstrainedUnionModel, JavaGenerator } from '../../../src';
+describe('JavaConstrainer', () => {
+  let renderer: JavaRenderer;
+  beforeEach(() => {
+    renderer = new MockJavaRenderer(JavaGenerator.defaultOptions, new JavaGenerator(), [], new CommonModel(), new CommonInputModel());
+  });
+  describe('ObjectModel', () => { 
+    test('should render the constrained name as type', () => {
+      const model = new ConstrainedObjectModel('test', undefined, '', {});
+      const type = JavaDefaultTypeMapping.Object({constrainedModel: model, renderer});
+      expect(type).toEqual(model.name);
+    });
+  });
+  describe('Reference', () => { 
+    test('should render the constrained name as type', () => {
+      const refModel = new ConstrainedAnyModel('test', undefined, '');
+      const model = new ConstrainedReferenceModel('test', undefined, '', refModel);
+      const type = JavaDefaultTypeMapping.Reference({constrainedModel: model, renderer});
+      expect(type).toEqual(model.name);
+    });
+  });
+  describe('Any', () => { 
+    test('should render type', () => {
+      const model = new ConstrainedAnyModel('test', undefined, '');
+      const type = JavaDefaultTypeMapping.Any({constrainedModel: model, renderer});
+      expect(type).toEqual('Object');
+    });
+  });
+  describe('Float', () => { 
+    test('should render type', () => {
+      const model = new ConstrainedFloatModel('test', undefined, '');
+      const type = JavaDefaultTypeMapping.Float({constrainedModel: model, renderer});
+      expect(type).toEqual('float');
+    });
+    test('should render double when original input has number format', () => {
+      const model = new ConstrainedFloatModel('test', {format: 'number'}, '');
+      const type = JavaDefaultTypeMapping.Float({constrainedModel: model, renderer});
+      expect(type).toEqual('double');
+    });
+    test('should render double when original input has double format', () => {
+      const model = new ConstrainedFloatModel('test', {format: 'double'}, '');
+      const type = JavaDefaultTypeMapping.Float({constrainedModel: model, renderer});
+      expect(type).toEqual('double');
+    });
+  });
+  describe('Integer', () => { 
+    test('should render type', () => {
+      const model = new ConstrainedIntegerModel('test', undefined, '');
+      const type = JavaDefaultTypeMapping.Integer({constrainedModel: model, renderer});
+      expect(type).toEqual('Integer');
+    });
+    test('should render int when original input has integer format', () => {
+      const model = new ConstrainedIntegerModel('test', {format: 'integer'}, '');
+      const type = JavaDefaultTypeMapping.Integer({constrainedModel: model, renderer});
+      expect(type).toEqual('int');
+    });
+    test('should render int when original input has int32 format', () => {
+      const model = new ConstrainedIntegerModel('test', {format: 'int32'}, '');
+      const type = JavaDefaultTypeMapping.Integer({constrainedModel: model, renderer});
+      expect(type).toEqual('int');
+    });
+    test('should render long when original input has long format', () => {
+      const model = new ConstrainedIntegerModel('test', {format: 'long'}, '');
+      const type = JavaDefaultTypeMapping.Integer({constrainedModel: model, renderer});
+      expect(type).toEqual('long');
+    });
+    test('should render long when original input has int64 format', () => {
+      const model = new ConstrainedIntegerModel('test', {format: 'int64'}, '');
+      const type = JavaDefaultTypeMapping.Integer({constrainedModel: model, renderer});
+      expect(type).toEqual('long');
+    });
+  });
+  describe('String', () => { 
+    test('should render type', () => {
+      const model = new ConstrainedStringModel('test', undefined, '');
+      const type = JavaDefaultTypeMapping.String({constrainedModel: model, renderer});
+      expect(type).toEqual('String');
+    });
+    test('should render LocalDate when original input has date format', () => {
+      const model = new ConstrainedStringModel('test', {format: 'date'}, '');
+      const type = JavaDefaultTypeMapping.String({constrainedModel: model, renderer});
+      expect(type).toEqual('java.time.LocalDate');
+    });
+    test('should render OffsetTime when original input has time format', () => {
+      const model = new ConstrainedStringModel('test', {format: 'time'}, '');
+      const type = JavaDefaultTypeMapping.String({constrainedModel: model, renderer});
+      expect(type).toEqual('java.time.OffsetTime');
+    });
+    test('should render OffsetDateTime when original input has dateTime format', () => {
+      const model = new ConstrainedStringModel('test', {format: 'dateTime'}, '');
+      const type = JavaDefaultTypeMapping.String({constrainedModel: model, renderer});
+      expect(type).toEqual('java.time.OffsetDateTime');
+    });
+    test('should render OffsetDateTime when original input has date-time format', () => {
+      const model = new ConstrainedStringModel('test', {format: 'date-time'}, '');
+      const type = JavaDefaultTypeMapping.String({constrainedModel: model, renderer});
+      expect(type).toEqual('java.time.OffsetDateTime');
+    });
+    test('should render byte when original input has binary format', () => {
+      const model = new ConstrainedStringModel('test', {format: 'binary'}, '');
+      const type = JavaDefaultTypeMapping.String({constrainedModel: model, renderer});
+      expect(type).toEqual('byte[]');
+    });
+  });
+  describe('Boolean', () => { 
+    test('should render type', () => {
+      const model = new ConstrainedBooleanModel('test', undefined, '');
+      const type = JavaDefaultTypeMapping.Boolean({constrainedModel: model, renderer});
+      expect(type).toEqual('Boolean');
+    });
+  });
+
+  describe('Tuple', () => { 
+    test('should render type', () => {
+      const model = new ConstrainedTupleModel('test', undefined, '', []);
+      const type = JavaDefaultTypeMapping.Tuple({constrainedModel: model, renderer});
+      expect(type).toEqual('Object[]');
+    });
+    test('should render tuple as list', () => {
+      const model = new ConstrainedTupleModel('test', undefined, '', []);
+      renderer.options.collectionType = 'List';
+      const type = JavaDefaultTypeMapping.Tuple({constrainedModel: model, renderer});
+      expect(type).toEqual('List<Object>');
+    });
+  });
+
+  describe('Array', () => { 
+    test('should render type', () => {
+      const arrayModel = new ConstrainedStringModel('test', undefined, 'String');
+      const model = new ConstrainedArrayModel('test', undefined, '', arrayModel);
+      renderer.options.collectionType = 'Array';
+      const type = JavaDefaultTypeMapping.Array({constrainedModel: model, renderer});
+      expect(type).toEqual('String[]');
+    });
+    test('should render array as a list', () => {
+      const arrayModel = new ConstrainedStringModel('test', undefined, 'String');
+      const model = new ConstrainedArrayModel('test', undefined, '', arrayModel);
+      renderer.options.collectionType = 'List';
+      const type = JavaDefaultTypeMapping.Array({constrainedModel: model, renderer});
+      expect(type).toEqual('List<String>');
+    });
+  });
+
+  describe('Enum', () => { 
+    test('should render the constrained name as type', () => {
+      const model = new ConstrainedEnumModel('test', undefined, '', []);
+      const type = JavaDefaultTypeMapping.Enum({constrainedModel: model, renderer});
+      expect(type).toEqual(model.name);
+    });
+  });
+
+  describe('Union', () => { 
+    test('should render type', () => {
+      const model = new ConstrainedUnionModel('test', undefined, '', []);
+      const type = JavaDefaultTypeMapping.Union({constrainedModel: model, renderer});
+      expect(type).toEqual('Object');
+    });
+  });
+
+  describe('Dictionary', () => { 
+    test('should render type', () => {
+      const keyModel = new ConstrainedStringModel('test', undefined, 'String');
+      const valueModel = new ConstrainedStringModel('test', undefined, 'String');
+      const model = new ConstrainedDictionaryModel('test', undefined, '', keyModel, valueModel);
+      const type = JavaDefaultTypeMapping.Dictionary({constrainedModel: model, renderer});
+      expect(type).toEqual('Map<String, String>');
+    });
+  });
+});
diff --git a/test/generators/java/JavaRenderer.spec.ts b/test/generators/java/JavaRenderer.spec.ts
index 12394d0e..3304c8ba 100644
--- a/test/generators/java/JavaRenderer.spec.ts
+++ b/test/generators/java/JavaRenderer.spec.ts
@@ -1,9 +1,8 @@
-import { defaultGeneratorOptions, JavaGenerator } from '../../../src/generators';
+import { JavaGenerator } from '../../../src/generators';
 import { JavaRenderer } from '../../../src/generators/java/JavaRenderer';
 import { CommonInputModel, CommonModel } from '../../../src/models';
-class MockJavaRenderer extends JavaRenderer {
+import { MockJavaRenderer } from '../../TestUtils/TestRenderers';
 
-}
 describe('JavaRenderer', () => {
   let renderer: JavaRenderer;
   beforeEach(() => {
diff --git a/test/generators/java/constrainer/EnumConstrainer.spec.ts b/test/generators/java/constrainer/EnumConstrainer.spec.ts
new file mode 100644
index 00000000..b6831af5
--- /dev/null
+++ b/test/generators/java/constrainer/EnumConstrainer.spec.ts
@@ -0,0 +1,94 @@
+import { JavaDefaultConstraints } from '../../../../src/generators/java/JavaConstrainer';
+import {EnumModel} from '../../../../src/models/MetaModel';
+import { ConstrainedEnumModel, ConstrainedEnumValueModel } from '../../../../src';
+import { defaultEnumKeyConstraints, ModelEnumKeyConstraints, DefaultEnumKeyConstraints } from '../../../../src/generators/java/constrainer/EnumConstrainer';
+describe('EnumConstrainer', () => {
+  const enumModel = new EnumModel('test', undefined, []);
+  const constrainedEnumModel = new ConstrainedEnumModel('test', undefined, '', []);
+
+  describe('enum keys', () => {
+    test('should never render special chars', () => {
+      const constrainedKey = JavaDefaultConstraints.enumKey({enumModel, constrainedEnumModel, enumKey: '%'});
+      expect(constrainedKey).toEqual('PERCENT');
+    });
+    test('should not render number as start char', () => {
+      const constrainedKey = JavaDefaultConstraints.enumKey({enumModel, constrainedEnumModel, enumKey: '1'});
+      expect(constrainedKey).toEqual('NUMBER_1');
+    });
+    test('should not contain duplicate keys', () => {
+      const existingConstrainedEnumValueModel = new ConstrainedEnumValueModel('TEST', 'test');
+      const constrainedEnumModel = new ConstrainedEnumModel('test', undefined, '', [existingConstrainedEnumValueModel]);
+      const constrainedKey = JavaDefaultConstraints.enumKey({enumModel, constrainedEnumModel, enumKey: 'TEST'});
+      expect(constrainedKey).toEqual('RESERVED_TEST');
+    });
+    test('should never contain empty keys', () => {
+      const constrainedKey = JavaDefaultConstraints.enumKey({enumModel, constrainedEnumModel, enumKey: ''});
+      expect(constrainedKey).toEqual('EMPTY');
+    });
+    test('should use constant naming format', () => {
+      const constrainedKey = JavaDefaultConstraints.enumKey({enumModel, constrainedEnumModel, enumKey: 'some weird_value!"#2'});
+      expect(constrainedKey).toEqual('SOME_WEIRD_VALUE_EXCLAMATION_QUOTATION_HASH_2');
+    });
+    test('should never render reserved keywords', () => {
+      const constrainedKey = JavaDefaultConstraints.enumKey({enumModel, constrainedEnumModel, enumKey: 'return'});
+      expect(constrainedKey).toEqual('RESERVED_RETURN');
+    });
+  });
+  describe('enum values', () => {
+    test('should render string values', () => {
+      const constrainedValue = JavaDefaultConstraints.enumValue({enumModel, constrainedEnumModel, enumValue: 'string value'});
+      expect(constrainedValue).toEqual('"string value"');
+    });
+    test('should render boolean values', () => {
+      const constrainedValue = JavaDefaultConstraints.enumValue({enumModel, constrainedEnumModel, enumValue: true});
+      expect(constrainedValue).toEqual('"true"');
+    });
+    test('should render numbers', () => {
+      const constrainedValue = JavaDefaultConstraints.enumValue({enumModel, constrainedEnumModel, enumValue: 123});
+      expect(constrainedValue).toEqual(123);
+    });
+    test('should render object', () => {
+      const constrainedValue = JavaDefaultConstraints.enumValue({enumModel, constrainedEnumModel, enumValue: {test: 'test'}});
+      expect(constrainedValue).toEqual('"{\\"test\\":\\"test\\"}"');
+    });
+    test('should render unknown value', () => {
+      const constrainedValue = JavaDefaultConstraints.enumValue({enumModel, constrainedEnumModel, enumValue: undefined});
+      expect(constrainedValue).toEqual('"undefined"');
+    });
+  });
+  describe('custom constraints', () => {
+    test('should be able to overwrite all hooks for enum key', () => {
+      const mockedConstraintCallbacks: ModelEnumKeyConstraints = {
+        NAMING_FORMATTER: jest.fn().mockReturnValue(''),
+        NO_SPECIAL_CHAR: jest.fn().mockReturnValue(''),
+        NO_NUMBER_START_CHAR: jest.fn().mockReturnValue(''),
+        NO_DUPLICATE_KEYS: jest.fn().mockReturnValue(''),
+        NO_EMPTY_VALUE: jest.fn().mockReturnValue(''),
+        NO_RESERVED_KEYWORDS: jest.fn().mockReturnValue('')
+      };
+      const constrainFunction = defaultEnumKeyConstraints(mockedConstraintCallbacks);
+      constrainFunction({enumModel, constrainedEnumModel, enumKey: ''});
+      //Expect all callbacks to be called
+      for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) {
+        expect(jestMockCallback).toHaveBeenCalled();
+      }
+    });
+    test('should be able to overwrite one hooks for enum key', () => {
+      //All but NAMING_FORMATTER, as we customize that
+      const spies = [
+        jest.spyOn(DefaultEnumKeyConstraints, 'NO_SPECIAL_CHAR'),
+        jest.spyOn(DefaultEnumKeyConstraints, 'NO_NUMBER_START_CHAR'),
+        jest.spyOn(DefaultEnumKeyConstraints, 'NO_DUPLICATE_KEYS'),
+        jest.spyOn(DefaultEnumKeyConstraints, 'NO_EMPTY_VALUE'),
+        jest.spyOn(DefaultEnumKeyConstraints, 'NO_RESERVED_KEYWORDS')
+      ];
+      const jestCallback = jest.fn().mockReturnValue('');
+      const constrainFunction = defaultEnumKeyConstraints({NAMING_FORMATTER: jestCallback});
+      const constrainedValue = constrainFunction({enumModel, constrainedEnumModel, enumKey: ''});
+      expect(constrainedValue).toEqual('');
+      for (const jestMockCallback of spies) {
+        expect(jestMockCallback).toHaveBeenCalled();
+      }
+    });
+  });
+});
diff --git a/test/generators/java/constrainer/ModelNameConstrainer.spec.ts b/test/generators/java/constrainer/ModelNameConstrainer.spec.ts
new file mode 100644
index 00000000..462b7d09
--- /dev/null
+++ b/test/generators/java/constrainer/ModelNameConstrainer.spec.ts
@@ -0,0 +1,58 @@
+import { JavaDefaultConstraints } from '../../../../src/generators/java/JavaConstrainer';
+import { defaultEnumKeyConstraints, ModelEnumKeyConstraints, DefaultEnumKeyConstraints } from '../../../../src/generators/java/constrainer/EnumConstrainer';
+import { DefaultModelNameConstraints, defaultModelNameConstraints, ModelNameConstraints } from '../../../../src/generators/java/constrainer/ModelNameConstrainer';
+describe('ModelNameConstrainer', () => {
+  test('should never render special chars', () => {
+    const constrainedKey = JavaDefaultConstraints.modelName({modelName: '%'});
+    expect(constrainedKey).toEqual('Percent');
+  });
+  test('should never render number as start char', () => {
+    const constrainedKey = JavaDefaultConstraints.modelName({modelName: '1'});
+    expect(constrainedKey).toEqual('Number_1');
+  });
+  test('should never contain empty name', () => {
+    const constrainedKey = JavaDefaultConstraints.modelName({modelName: ''});
+    expect(constrainedKey).toEqual('Empty');
+  });
+  test('should use constant naming format', () => {
+    const constrainedKey = JavaDefaultConstraints.modelName({modelName: 'some weird_value!"#2'});
+    expect(constrainedKey).toEqual('SomeWeirdValueExclamationQuotationHash_2');
+  });
+  test('should never render reserved keywords', () => {
+    const constrainedKey = JavaDefaultConstraints.modelName({modelName: 'return'});
+    expect(constrainedKey).toEqual('ReservedReturn');
+  });
+  describe('custom constraints', () => {
+    test('should be able to overwrite all hooks', () => {
+      const mockedConstraintCallbacks: ModelNameConstraints = {
+        NAMING_FORMATTER: jest.fn().mockReturnValue(''),
+        NO_SPECIAL_CHAR: jest.fn().mockReturnValue(''),
+        NO_NUMBER_START_CHAR: jest.fn().mockReturnValue(''),
+        NO_EMPTY_VALUE: jest.fn().mockReturnValue(''),
+        NO_RESERVED_KEYWORDS: jest.fn().mockReturnValue('')
+      };
+      const constrainFunction = defaultModelNameConstraints(mockedConstraintCallbacks);
+      constrainFunction({modelName: ''});
+      //Expect all callbacks to be called
+      for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) {
+        expect(jestMockCallback).toHaveBeenCalled();
+      }
+    });
+    test('should be able to overwrite one hooks', () => {
+      //All but NAMING_FORMATTER, as we customize that
+      const spies = [
+        jest.spyOn(DefaultModelNameConstraints, 'NO_SPECIAL_CHAR'),
+        jest.spyOn(DefaultModelNameConstraints, 'NO_NUMBER_START_CHAR'),
+        jest.spyOn(DefaultModelNameConstraints, 'NO_EMPTY_VALUE'),
+        jest.spyOn(DefaultModelNameConstraints, 'NO_RESERVED_KEYWORDS')
+      ];
+      const jestCallback = jest.fn().mockReturnValue('');
+      const constrainFunction = defaultModelNameConstraints({NAMING_FORMATTER: jestCallback});
+      const constrainedValue = constrainFunction({modelName: ''});
+      expect(constrainedValue).toEqual('');
+      for (const jestMockCallback of spies) {
+        expect(jestMockCallback).toHaveBeenCalled();
+      }
+    });
+  });
+});
diff --git a/test/generators/java/constrainer/PropertyKeyConstrainer.spec.ts b/test/generators/java/constrainer/PropertyKeyConstrainer.spec.ts
new file mode 100644
index 00000000..c5f300cd
--- /dev/null
+++ b/test/generators/java/constrainer/PropertyKeyConstrainer.spec.ts
@@ -0,0 +1,70 @@
+import { JavaDefaultConstraints } from '../../../../src/generators/java/JavaConstrainer';
+import { ConstrainedObjectModel, ObjectModel } from '../../../../src';
+import { DefaultPropertyKeyConstraints, defaultPropertyKeyConstraints, PropertyKeyConstraintOptions } from '../../../../src/generators/java/constrainer/PropertyKeyConstrainer';
+describe('PropertyKeyConstrainer', () => {
+  const objectModel = new ObjectModel('test', undefined, {});
+  const constrainedObjectModel = new ConstrainedObjectModel('test', undefined, '', {});
+
+  test('should never render special chars', () => {
+    const constrainedKey = JavaDefaultConstraints.propertyKey({constrainedObjectModel, objectModel, propertyKey: '%'});
+    expect(constrainedKey).toEqual('Percent');
+  });
+  test('should not render number as start char', () => {
+    const constrainedKey = JavaDefaultConstraints.propertyKey({constrainedObjectModel, objectModel, propertyKey: '1'});
+    expect(constrainedKey).toEqual('Number_1');
+  });
+  test('should never contain empty name', () => {
+    const constrainedKey = JavaDefaultConstraints.propertyKey({constrainedObjectModel, objectModel, propertyKey: ''});
+    expect(constrainedKey).toEqual('Empty');
+  });
+  test('should use constant naming format', () => {
+    const constrainedKey = JavaDefaultConstraints.propertyKey({constrainedObjectModel, objectModel, propertyKey: 'some weird_value!"#2'});
+    expect(constrainedKey).toEqual('SomeWeirdValueExclamationQuotationHash_2');
+  });
+  test('should not contain duplicate properties', () => {
+    const objectModel = new ObjectModel('test', undefined, {});
+    const propertyModel = new ConstrainedObjectModel('SomeProperty', undefined, '', {});
+    const constrainedObjectModel = new ConstrainedObjectModel('test', undefined, '', {SomeProperty: propertyModel});
+    const constrainedKey = JavaDefaultConstraints.propertyKey({constrainedObjectModel, objectModel, propertyKey: 'SomeProperty'});
+    expect(constrainedKey).toEqual('ReservedSomeProperty');
+  });
+  test('should never render reserved keywords', () => {
+    const constrainedKey = JavaDefaultConstraints.propertyKey({constrainedObjectModel, objectModel, propertyKey: 'return'});
+    expect(constrainedKey).toEqual('ReservedReturn');
+  });
+  describe('custom constraints', () => {
+    test('should be able to overwrite all hooks', () => {
+      const mockedConstraintCallbacks: PropertyKeyConstraintOptions = {
+        NAMING_FORMATTER: jest.fn().mockReturnValue(''),
+        NO_SPECIAL_CHAR: jest.fn().mockReturnValue(''),
+        NO_NUMBER_START_CHAR: jest.fn().mockReturnValue(''),
+        NO_EMPTY_VALUE: jest.fn().mockReturnValue(''),
+        NO_RESERVED_KEYWORDS: jest.fn().mockReturnValue(''),
+        NO_DUPLICATE_PROPERTIES: jest.fn().mockReturnValue('')
+      };
+      const constrainFunction = defaultPropertyKeyConstraints(mockedConstraintCallbacks);
+      constrainFunction({constrainedObjectModel, objectModel, propertyKey: ''});
+      //Expect all callbacks to be called
+      for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) {
+        expect(jestMockCallback).toHaveBeenCalled();
+      }
+    });
+    test('should be able to overwrite one hooks', () => {
+      //All but NAMING_FORMATTER, as we customize that
+      const spies = [
+        jest.spyOn(DefaultPropertyKeyConstraints, 'NO_SPECIAL_CHAR'),
+        jest.spyOn(DefaultPropertyKeyConstraints, 'NO_NUMBER_START_CHAR'),
+        jest.spyOn(DefaultPropertyKeyConstraints, 'NO_EMPTY_VALUE'),
+        jest.spyOn(DefaultPropertyKeyConstraints, 'NO_RESERVED_KEYWORDS'),
+        jest.spyOn(DefaultPropertyKeyConstraints, 'NO_DUPLICATE_PROPERTIES')
+      ];
+      const jestCallback = jest.fn().mockReturnValue('');
+      const constrainFunction = defaultPropertyKeyConstraints({NAMING_FORMATTER: jestCallback});
+      const constrainedValue = constrainFunction({constrainedObjectModel, objectModel, propertyKey: ''});
+      expect(constrainedValue).toEqual('');
+      for (const jestMockCallback of spies) {
+        expect(jestMockCallback).toHaveBeenCalled();
+      }
+    });
+  });
+});
diff --git a/test/helpers/Constraints.spec.ts b/test/helpers/Constraints.spec.ts
new file mode 100644
index 00000000..63a6028e
--- /dev/null
+++ b/test/helpers/Constraints.spec.ts
@@ -0,0 +1,25 @@
+import { NO_NUMBER_START_CHAR, NO_EMPTY_VALUE } from '../../src/helpers';
+
+describe('Constraints', () => {
+  describe('NO_NUMBER_START_CHAR', () => {
+    test('should not prepend anything to empty values', () => {
+      const renderedValue = NO_NUMBER_START_CHAR('');
+      expect(renderedValue).toEqual('');
+    });
+    test('should prepend something to numbers', () => {
+      const renderedValue = NO_NUMBER_START_CHAR('1');
+      expect(renderedValue).toEqual('number_1');
+    });
+  });
+
+  describe('NO_EMPTY_VALUE', () => {
+    test('should not allow empty values', () => {
+      const renderedValue = NO_EMPTY_VALUE('');
+      expect(renderedValue).toEqual('empty');
+    });
+    test('should not do anything to nonempty values', () => {
+      const renderedValue = NO_EMPTY_VALUE('1');
+      expect(renderedValue).toEqual('1');
+    });
+  });
+});
diff --git a/test/helpers/TypeHelpers.spec.ts b/test/helpers/TypeHelpers.spec.ts
index 7283d2f5..bf398053 100644
--- a/test/helpers/TypeHelpers.spec.ts
+++ b/test/helpers/TypeHelpers.spec.ts
@@ -1,5 +1,6 @@
-import { TypeHelpers, ModelKind } from '../../src/helpers'; 
-import { CommonModel } from '../../src/models';
+import { TypeHelpers, ModelKind, getTypeFromMapping, TypeMapping } from '../../src/helpers'; 
+import { CommonModel, ConstrainedAnyModel, ConstrainedArrayModel, ConstrainedBooleanModel, ConstrainedDictionaryModel, ConstrainedEnumModel, ConstrainedFloatModel, ConstrainedIntegerModel, ConstrainedMetaModel, ConstrainedObjectModel, ConstrainedReferenceModel, ConstrainedStringModel, ConstrainedTupleModel, ConstrainedUnionModel } from '../../src/models';
+import { TestRenderer } from '../TestUtils/TestRenderers';
 
 describe('TypeHelpers', () => {
   describe('extractKind', () => {
@@ -55,4 +56,47 @@ describe('TypeHelpers', () => {
       expect(kind).toEqual(ModelKind.PRIMITIVE);
     });
   });
+  describe('getTypeFromMapping', () => {
+    const testRenderer = new TestRenderer();
+    const typeMapping: TypeMapping<TestRenderer> = {
+      Object: jest.fn().mockReturnValue('test'),
+      Reference: jest.fn().mockReturnValue('test'),
+      Any: jest.fn().mockReturnValue('test'),
+      Float: jest.fn().mockReturnValue('test'), 
+      Integer: jest.fn().mockReturnValue('test'),
+      String: jest.fn().mockReturnValue('test'),
+      Boolean: jest.fn().mockReturnValue('test'),
+      Tuple: jest.fn().mockReturnValue('test'),
+      Array: jest.fn().mockReturnValue('test'),
+      Enum: jest.fn().mockReturnValue('test'),
+      Union: jest.fn().mockReturnValue('test'),
+      Dictionary: jest.fn().mockReturnValue('test')
+    };
+    test('should return undefined with generic constrained model', () => {
+      const constrainedModel = new ConstrainedMetaModel('', undefined, '');
+
+      const t = () => {
+        getTypeFromMapping(typeMapping, {constrainedModel, renderer: testRenderer});
+      };
+      expect(t).toThrow('Could not find type for model');
+    });
+    const modelsToCheck = [
+      new ConstrainedObjectModel('', undefined, '', {}),
+      new ConstrainedReferenceModel('', undefined, '', new ConstrainedMetaModel('', undefined, '')),
+      new ConstrainedAnyModel('', undefined, ''),
+      new ConstrainedFloatModel('', undefined, ''),
+      new ConstrainedIntegerModel('', undefined, ''),
+      new ConstrainedStringModel('', undefined, ''),
+      new ConstrainedBooleanModel('', undefined, ''),
+      new ConstrainedTupleModel('', undefined, '', []),
+      new ConstrainedArrayModel('', undefined, '', new ConstrainedMetaModel('', undefined, '')),
+      new ConstrainedEnumModel('', undefined, '', []),
+      new ConstrainedUnionModel('', undefined, '', []),
+      new ConstrainedDictionaryModel('', undefined, '', new ConstrainedMetaModel('', undefined, ''), new ConstrainedMetaModel('', undefined, ''))
+    ];
+    test.each(modelsToCheck)('should return type from mapping', (constrainedModel: ConstrainedMetaModel) => {
+      const foundType = getTypeFromMapping(typeMapping, {constrainedModel, renderer: testRenderer});
+      expect(foundType).toEqual('test');
+    });
+  });
 });