diff --git a/[Help b/[Help
new file mode 100644
index 000000000..e69de29bb
diff --git a/nondex-workflow.diff b/nondex-workflow.diff
new file mode 100644
index 000000000..9b462dafa
--- /dev/null
+++ b/nondex-workflow.diff
@@ -0,0 +1,45141 @@
+diff --git a/.github/dependabot.yml b/.github/dependabot.yml
+deleted file mode 100644
+index 512939f9c..000000000
+--- a/.github/dependabot.yml
++++ /dev/null
+@@ -1,7 +0,0 @@
+-version: 2
+-updates:
+- - package-ecosystem: "maven"
+- directory: "/"
+- schedule:
+- interval: "monthly"
+- open-pull-requests-limit: 10
+diff --git a/.github/workflows/nondex-test.yml b/.github/workflows/nondex-test.yml
+new file mode 100644
+index 000000000..809113809
+--- /dev/null
++++ b/.github/workflows/nondex-test.yml
+@@ -0,0 +1,35 @@
++# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
++# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven
++
++# This workflow uses actions that are not certified by GitHub.
++# They are provided by a third-party and are governed by
++# separate terms of service, privacy policy, and support
++# documentation.
++
++name: Java CI with Maven
++
++on: push
++
++jobs:
++ build:
++
++ runs-on: ubuntu-latest
++
++ steps:
++ - uses: actions/checkout@v4
++ - name: Set up JDK 8
++ uses: actions/setup-java@v4
++ with:
++ java-version: '8'
++ distribution: 'temurin'
++ cache: maven
++ - name: Without Nondex
++ run: mvn -pl typescript-generator-core test -Dtest=cz.habarta.typescript.generator.TaggedUnionsTest#testTaggedUnionsWithInterfaces
++ - name: Nondex
++ run: mvn edu.illinois:nondex-maven-plugin:1.1.2:nondex \
++ -Dtest=cz.habarta.typescript.generator.TaggedUnionsTest#testTaggedUnionsWithInterfaces \
++ -DnondexRuns=10
++
++ # Optional: Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive
++ - name: Update dependency graph
++ uses: advanced-security/maven-dependency-submission-action@571e99aab1055c2e71a1e2309b9691de18d6b7d6
+diff --git a/.github/workflows/release-gradle-plugin.yml b/.github/workflows/release-gradle-plugin.yml
+deleted file mode 100644
+index 1fd4ccce1..000000000
+--- a/.github/workflows/release-gradle-plugin.yml
++++ /dev/null
+@@ -1,35 +0,0 @@
+-
+-name: Release to Gradle plugin portal
+-
+-on:
+- workflow_dispatch:
+- inputs:
+- version:
+- description: typescript-generator version
+- required: true
+- type: string
+-
+-jobs:
+- release:
+- runs-on: windows-latest
+- steps:
+-
+- - name: Checkout workflow
+- uses: actions/checkout@v3
+-
+- - name: Setup Java
+- uses: actions/setup-java@v3
+- with:
+- distribution: temurin
+- java-version: 11
+-
+- - name: Execute Gradle publish
+- uses: gradle/gradle-build-action@v2
+- with:
+- gradle-version: 7.5.1
+- build-root-directory: build\typescript-generator-gradle-plugin-publisher
+- arguments: printVersion publishPlugins
+- env:
+- ORG_GRADLE_PROJECT_version: ${{ inputs.version }}
+- GRADLE_PUBLISH_KEY: ${{ secrets.GRADLE_PUBLISH_KEY }}
+- GRADLE_PUBLISH_SECRET: ${{ secrets.GRADLE_PUBLISH_SECRET }}
+diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
+deleted file mode 100644
+index 2500e684f..000000000
+--- a/.github/workflows/release.yml
++++ /dev/null
+@@ -1,32 +0,0 @@
+-
+-name: Release to Maven Central
+-
+-on:
+- workflow_dispatch:
+-
+-jobs:
+- release:
+- runs-on: windows-latest
+-
+- steps:
+- - name: Checkout workflow
+- uses: actions/checkout@v2
+- - name: Setup GPG signing key
+- run: |
+- if (-not $env:GPG_SIGNING_KEY) {
+- Write-Error "Please set GPG_SIGNING_KEY environment variable"
+- exit 1
+- }
+- mkdir target -Force | Out-Null
+- $gpg_signing_key = [Convert]::FromBase64String($env:GPG_SIGNING_KEY)
+- [System.IO.File]::WriteAllBytes("target/gpg_signing_key.bin", $gpg_signing_key)
+- gpg --import target/gpg_signing_key.bin
+- gpg --list-secret-keys
+- env:
+- GPG_SIGNING_KEY: ${{ secrets.GPG_SIGNING_KEY }}
+- - name: Sign and upload
+- run: |
+- ./build/release-build.ps1
+- env:
+- OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
+- OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
+diff --git a/.gitignore b/.gitignore
+index 0195d9e5e..6a3a87d8f 100644
+--- a/.gitignore
++++ b/.gitignore
+@@ -1,45 +1,16 @@
+-# Maven
+ target/
+ pom.xml.tag
+ pom.xml.releaseBackup
+ pom.xml.versionsBackup
+ pom.xml.next
+ release.properties
+-
+-# Gradle
+ /sample-gradle/.gradle/
+ /sample-gradle/.nb-gradle/
+-/sample-gradle/bin/
+ /sample-gradle/build/
+-/sample-gradle-spring/.gradle/
+-/sample-gradle-spring/.nb-gradle/
+-/sample-gradle-spring/bin/
+-/sample-gradle-spring/build/
+-.gradle
+-gradlew*
+-gradle-wrapper*
+-
+-# nodejs
+-node/
+-
+-# npm
+-node_modules
+-
+-# NetBeans
+-nbactions.xml
+-
+-# Eclipse
+ **.classpath
+ **.project
+ **.settings
+
+-# JetBrains IntelliJ
++# JetBrain IntelliJ
+ .idea/
+-*.iml
+-
+-# VS Code
+-.vscode
+-
+-# generated files
+-typescript-generator-core/src/test/**/*.js
+-typescript-generator-core/src/test/**/*.js.map
++**.iml
+\ No newline at end of file
+diff --git a/README.md b/README.md
+index c3308e300..0561cc6d1 100644
+--- a/README.md
++++ b/README.md
+@@ -1,20 +1,7 @@
+-[](https://repo1.maven.org/maven2/cz/habarta/typescript-generator/typescript-generator-core/)
+-[](https://ci.appveyor.com/project/vojtechhabarta/typescript-generator)
+-[](https://github.com/vojtechhabarta/typescript-generator)
+-
+-Quick links:
+-[Configuration parameters](https://www.habarta.cz/typescript-generator/maven/typescript-generator-maven-plugin/generate-mojo.html)
+-|
+-[Breaking changes](https://github.com/vojtechhabarta/typescript-generator/wiki/Breaking-Changes)
+-|
+-[Release notes](https://github.com/vojtechhabarta/typescript-generator/releases)
+-|
+-[Playground _(beta)_](https://jechlin.github.io/ts-gen-aws/)
+-
+ typescript-generator
+ ====================
+ typescript-generator is a tool for generating TypeScript definition files (.d.ts) from Java JSON classes.
+-If you have REST service written in Java (or another JVM language) using object to JSON mapping you can use typescript-generator to generate TypeScript interfaces from Java classes.
++If you have REST service written in Java using object to JSON mapping you can use typescript-generator to generate TypeScript interfaces from Java classes.
+
+ For example for this Java class:
+
+@@ -68,16 +55,17 @@ In Maven build you can use `typescript-generator-maven-plugin` like this:
+
+ generate
+
+- process-classes
++
++ jackson2
++
++ cz.habarta.typescript.generator.Person
++
++ target/rest.d.ts
++ global
++ Rest
++
+
+
+-
+- jackson2
+-
+- cz.habarta.typescript.generator.Person
+-
+- module
+-
+
+ ```
+
+@@ -90,44 +78,26 @@ Gradle
+
+ In Gradle build you can use `cz.habarta.typescript-generator` plugin like this:
+ ```groovy
+-plugins {
+- id 'cz.habarta.typescript-generator' version 'x.y.z'
++apply plugin: 'cz.habarta.typescript-generator'
++buildscript {
++ repositories {
++ mavenCentral()
++ }
++ dependencies {
++ classpath group: 'cz.habarta.typescript-generator', name: 'typescript-generator-gradle-plugin', version: 'x.y.z'
++ }
+ }
+-
+ generateTypeScript {
+ jsonLibrary = 'jackson2'
+ classes = [
+ 'cz.habarta.typescript.generator.sample.Person'
+ ]
+- outputKind = 'module'
++ outputFile = 'build/sample.d.ts'
++ outputKind = 'global'
++ namespace = 'Rest';
+ }
+ ```
+
+-For the Kotlin Gradle DSL you can alternatively use the `cz.habarta.typescript-generator` plugin like this:
+-
+-#### build.gradle.kts
+-```kotlin
+-import cz.habarta.typescript.generator.JsonLibrary
+-import cz.habarta.typescript.generator.TypeScriptFileType
+-import cz.habarta.typescript.generator.TypeScriptOutputKind
+-
+-plugins {
+- id("cz.habarta.typescript-generator") version "x.y.z"
+-}
+-
+-tasks {
+- generateTypeScript {
+- jsonLibrary = JsonLibrary.jackson2
+- outputKind = TypeScriptOutputKind.module
+- outputFileType = TypeScriptFileType.implementationFile
+- ...
+- }
+-}
+-```
+-
+-You can run typescript-generator on demand using `gradle generateTypeScript` command
+-or you can invoke it as part of another task by adding dependency from that task to `generateTypeScript` task in Gradle build file.
+-
+ More complete sample can be found [here](sample-gradle).
+ Gradle plugin has the same features as Maven plugin, for detailed description see Maven generated [site](http://vojtechhabarta.github.io/typescript-generator/maven/typescript-generator-maven-plugin/generate-mojo.html).
+
+@@ -144,7 +114,7 @@ Input classes can be specified using several parameters:
+ - **`classPatterns`** - list of glob patterns like `com.example.*Json`, includes all classes matched by the pattern, supported are `*` and `**` wildcards
+ - **`classesFromJaxrsApplication`** - fully qualified name of JAX-RS application class, all classes used by application resources will be included, recommended if you have JAX-RS application class
+ - **`classesFromAutomaticJaxrsApplication`** - value `true` will include classes from automatically discovered REST resources, recommended if you have JAX-RS application without `Application` subclass
+-- **`excludeClasses`** - list of fully qualified class names, excluded classes will be mapped to TypeScript `any` type, if excluded class is a resource then this resource will not be scanned for used classes
++- **`excludeClasses`** - list of fully qualified class names, excluded classes will be mapped to TypeScript `any` type, if exluded class is a resource then this resource will not be scanned for used classes
+
+ > Note: it is possible to use multiple parameters at the same time.
+
+@@ -153,39 +123,14 @@ For more details see [Class Names Glob Patterns](../../wiki/Class-Names-Glob-Pat
+
+ Output parameters
+ -----------------
+-Output is configured using several parameters:
+-- `outputKind` (required parameter) - determines if and how module will be generated
++Output is configured using several parameters, two of them are mandatory:
++- `outputFile` - specifies path and name of output file
++- `outputKind` - determines if and how module will be generated
+ - values are: `global`, `module`, `ambientModule`
+-- `outputFileType` - specifies TypeScript file type
+- - values are: `declarationFile` (.d.ts) or `implementationFile` (.ts)
+-- `outputFile` - specifies path and name of output file
+
+ For more details see [Modules and Namespaces](http://vojtechhabarta.github.io/typescript-generator/doc/ModulesAndNamespaces.html) page.
+
+
+-REST frameworks
+----------------
+-Typescript-generator can generate not only TypeScript declarations for JSON Java classes but it can also generate client classes for REST services. Supported REST frameworks are JAX-RS and Spring. Client for JAX-RS service can be generated using `generateJaxrsApplicationClient` parameter, client for Spring service can be generated using `generateSpringApplicationClient`. Since Spring support is in separate module it is needed to add this module to typescript-generator dependencies. Here is example for Maven:
+-``` xml
+-
+- cz.habarta.typescript-generator
+- typescript-generator-maven-plugin
+- ${typescript-generator.version}
+-
+- true
+- ...
+-
+-
+-
+- cz.habarta.typescript-generator
+- typescript-generator-spring
+- ${typescript-generator.version}
+-
+-
+-
+-```
+-
+-
+ Download
+ --------
+ Releases are available from Maven Central Repository.
+@@ -206,7 +151,7 @@ Architecture
+ ```
+ (Model) (TsModel)
+ ModelParser ==> ModelCompiler ==> Emitter
+- | |
++ | |
+ V V
+ TypeProcessor
+ ```
+@@ -220,23 +165,15 @@ ModelParser ==> ModelCompiler ==> Emitter
+ - `Emitter` takes `TsModel` and produces TypeScript declaration file.
+
+
+-Links
+------
+-
+-- http://www.rainerhahnekamp.com/type-safe-endpoints-with-typescript-and-java - blog post about using typescript-generator not only with Spring MVC
+-- http://www.jsweet.org/10-reasons-to-use-jsweet - blog post about JSweet transpiler mentions typescript-generator
+-- https://github.com/raphaeljolivet/java2typescript - tool similar to typescript-generator
+-
+-
+ Contributing
+ ------------
+
+-- current major version supports Java 8 and later (version 1 supported Java 7 and 8)
++- this project targets Java 7
+ - keep pull requests small and focused ([10 tips for better Pull Requests](http://blog.ploeh.dk/2015/01/15/10-tips-for-better-pull-requests/))
+ - do not add dependencies unless previously discussed in issue
+
+ ### Code formatting
+
+ - use 4 spaces for indentation in Java files
+-- sort java imports alphabetically (including static imports), do not use wildcard (star) imports
+-- please do not reformat whole files in IDE (prevent accidental changes to unrelated lines)
++- sort java imports alphabetically, you can use wildcards
++- please do not reformat whole files in IDE
+diff --git a/appveyor.yml b/appveyor.yml
+index 2852ddce4..d5049a2a6 100644
+--- a/appveyor.yml
++++ b/appveyor.yml
+@@ -1,15 +1,9 @@
+-version: 3.2.{build}
++version: 1.12.{build}
+ pull_requests:
+ do_not_increment_build_number: true
+ skip_tags: true
+-cache:
+-- '%USERPROFILE%\.m2\repository -> appveyor.yml'
+-init:
+-- ps: |-
+- if ($env:APPVEYOR_REPO_BRANCH -ne "main" -or $env:APPVEYOR_PULL_REQUEST_NUMBER)
+- {
+- $env:APPVEYOR_CACHE_SKIP_SAVE = "true"
+- }
++environment:
++ JAVA_HOME: C:\Program Files\Java\jdk1.7.0
+ install:
+ - ps: |-
+ git remote set-url origin https://github.com/vojtechhabarta/typescript-generator.git
+@@ -18,14 +12,13 @@ install:
+ build_script:
+ - ps: |-
+ Write-Host -ForegroundColor Cyan "Building version: $($env:APPVEYOR_BUILD_VERSION)"
+- $env:JAVA_HOME = "C:\Program Files\Java\jdk11"
+- $env:PATH = "$env:JAVA_HOME\bin;$env:PATH"
+- mvn -version
+- mvn --batch-mode versions:set "-DnewVersion=$($env:APPVEYOR_BUILD_VERSION)"; if ($LASTEXITCODE -ne 0) { throw "Build failed" }
+- mvn --batch-mode -P attach-artifacts clean install -P local-deploy; if ($LASTEXITCODE -ne 0) { throw "Build failed" }
+- if ($env:APPVEYOR_REPO_BRANCH -eq "main" -and -not $env:APPVEYOR_PULL_REQUEST_NUMBER)
++ choco install maven -version 3.3.9.1 -y
++ $mvn = "C:\tools\apache-maven-3.3.9\bin\mvn.cmd"
++ &$mvn --batch-mode versions:set "-DnewVersion=$($env:APPVEYOR_BUILD_VERSION)"; if ($LASTEXITCODE -ne 0) { throw "Build failed" }
++ &$mvn --batch-mode -P attach-artifacts clean install -P local-deploy; if ($LASTEXITCODE -ne 0) { throw "Build failed" }
++ if ($env:APPVEYOR_REPO_BRANCH -eq "master" -and -not $env:APPVEYOR_PULL_REQUEST_NUMBER)
+ {
+- mvn --batch-mode site-deploy "-Dgithub.site.oauth2Token=$($env:access_token)"; if ($LASTEXITCODE -ne 0) { throw "Build failed" }
++ &$mvn --batch-mode site-deploy "-Dgithub.site.oauth2Token=$($env:access_token)"; if ($LASTEXITCODE -ne 0) { throw "Build failed" }
+ }
+
+ Write-Host -ForegroundColor Cyan "Archiving artifacts"
+@@ -37,7 +30,7 @@ artifacts:
+ - path: target\artifacts.zip
+ on_success:
+ - ps: |-
+- if ($env:APPVEYOR_REPO_BRANCH -eq "main" -and -not $env:APPVEYOR_PULL_REQUEST_NUMBER)
++ if ($env:APPVEYOR_REPO_BRANCH -eq "master" -and -not $env:APPVEYOR_PULL_REQUEST_NUMBER)
+ {
+ Write-Host -ForegroundColor Cyan "Tagging version v$($env:APPVEYOR_BUILD_VERSION)"
+ git tag "v$($env:APPVEYOR_BUILD_VERSION)" $env:APPVEYOR_REPO_COMMIT
+diff --git a/build/download-appveyor-artifacts.ps1 b/build/download-appveyor-artifacts.ps1
+index 9ddea4b85..9c7be6805 100644
+--- a/build/download-appveyor-artifacts.ps1
++++ b/build/download-appveyor-artifacts.ps1
+@@ -9,7 +9,7 @@ $jobId = $project.build.jobs[0].jobId
+ $artifactsUri = "$apiUrl/buildjobs/$jobId/artifacts/target/artifacts.zip"
+ Write-Host -ForegroundColor DarkCyan "Downloading '$artifactsUri'..."
+ $zipFilePath = "target\artifacts.zip"
+-$zipFileDirectory = New-Item -ItemType directory -Path (Split-Path $zipFilePath) -Force
++$zipFileDirectory = mkdir (Split-Path $zipFilePath) -Force
+ Invoke-RestMethod -Method Get -Uri $artifactsUri -OutFile $zipFilePath
+ $zipFile = Get-Item $zipFilePath
+ $zipFile.FullName
+diff --git a/build/how-to-release.md b/build/how-to-release.md
+deleted file mode 100644
+index e833f678a..000000000
+--- a/build/how-to-release.md
++++ /dev/null
+@@ -1,15 +0,0 @@
+-# How to release typescript-generator
+-
+-- change version in `pom.xml` files and `appveyor.yml` (if not already changed)
+-- wait for the build
+-- run "Release to Maven Central" GitHub Action which releases last build
+-- go to https://oss.sonatype.org and promote the release
+- - "Staging Repositories"
+- - "Close" the repo
+- - wait for closing activities
+- - "Release" the repo
+-- wait for the release to appear in Maven Central - https://repo1.maven.org/maven2/cz/habarta/typescript-generator/
+-- write release notes
+-- run "Release to Gradle plugin portal" GitHub Action
+-- close/update relevant issues and PRs
+-- remove unused tags
+diff --git a/build/release-build.ps1 b/build/release-build.ps1
+index f9d41d852..3b17d66ed 100644
+--- a/build/release-build.ps1
++++ b/build/release-build.ps1
+@@ -1,51 +1,52 @@
+
+-# Releases latest build to Maven Central Repository
+-# Prerequisites:
+-# - gpg with signing key without passphrase
+-# - OSSRH_USERNAME
+-# - OSSRH_PASSWORD
+-
+-if (-not $env:OSSRH_USERNAME -or -not $env:OSSRH_PASSWORD) {
+- Write-Error "Please set OSSRH_USERNAME and OSSRH_PASSWORD environment variables"
+- exit 1
+-}
+-
+ $ErrorActionPreference = "Stop"
+
+ # download
+-$zipFile = ./build/download-appveyor-artifacts.ps1
++$zipFile = .\build\download-appveyor-artifacts.ps1
+
+ # unzip
+ Write-Host -ForegroundColor DarkCyan "Unzipping..."
+-$unzipDirectoryPath = "target/gpg-sign"
+-Remove-Item -Recurse -Force $unzipDirectoryPath -ErrorAction SilentlyContinue
+-Remove-Item -Recurse -Force $unzipDirectoryPath -ErrorAction SilentlyContinue
+-$unzipDirectory = New-Item -ItemType directory -Path $unzipDirectoryPath -Force
++$unzipDirectoryPath = "target\gpg-sign"
++rm -Recurse -Force $unzipDirectoryPath -ErrorAction SilentlyContinue
++rm -Recurse -Force $unzipDirectoryPath -ErrorAction SilentlyContinue
++$unzipDirectory = mkdir $unzipDirectoryPath -Force
+ [System.Reflection.Assembly]::LoadWithPartialName("System.IO.Compression.FileSystem") | Out-Null
+ [System.IO.Compression.ZipFile]::ExtractToDirectory($zipFile, $unzipDirectory.FullName)
+ $basePath = (Resolve-Path $unzipDirectory).Path
+
++# passphrase
++$securePassphrase = Read-Host -Prompt "Enter signing key passphrase" -AsSecureString
++$passphraseCredential = New-Object System.Management.Automation.PSCredential -ArgumentList "Domain\User", $securePassphrase
++$passphrase = $passphraseCredential.GetNetworkCredential().Password
++"test" | gpg --detach-sign --armor --passphrase $passphrase | Out-Null
++if (! $?) {
++ exit
++}
++
+ # sign
+-foreach ($file in Get-ChildItem -Recurse -File $basePath -Exclude *.md5,*.sha1) {
++foreach ($file in dir -Recurse -File $basePath -Exclude *.md5,*.sha1) {
+ $path = $file.FullName.Substring($basePath.Length + 1)
+ Write-Host -ForegroundColor DarkCyan "Signing $path..."
+- gpg --detach-sign --armor $file.FullName
+- Get-FileHash -Algorithm MD5 $file.FullName | ForEach-Object { [IO.File]::WriteAllText($file.FullName + ".md5", $_.Hash) }
+- Get-FileHash -Algorithm SHA1 $file.FullName | ForEach-Object { [IO.File]::WriteAllText($file.FullName + ".sha1", $_.Hash) }
++ gpg --detach-sign --armor --passphrase $passphrase $file.FullName
++ Get-FileHash -Algorithm MD5 $file.FullName | % { [IO.File]::WriteAllText($file.FullName + ".md5", $_.Hash) }
++ Get-FileHash -Algorithm SHA1 $file.FullName | % { [IO.File]::WriteAllText($file.FullName + ".sha1", $_.Hash) }
+ }
+
+ # upload
+ $repoUri = "https://oss.sonatype.org/service/local/staging/deploy/maven2"
+-$credential = New-Object -TypeName System.Net.NetworkCredential -ArgumentList $env:OSSRH_USERNAME, $env:OSSRH_PASSWORD
+-$headers = @{"Authorization" = "Basic " + [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($credential.UserName + ":" + $credential.Password))}
+-foreach ($file in Get-ChildItem -Recurse -File $basePath) {
++$credential = $host.UI.PromptForCredential("OSS Repository Hosting", "Enter your credentials for Sonatype maven repository https://oss.sonatype.org", "", "")
++if (! $credential) {
++ exit
++}
++$headers = @{"Authorization" = "Basic " + [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($credential.UserName + ":" + $credential.GetNetworkCredential().Password))}
++foreach ($file in dir -Recurse -File $basePath) {
+ $path = $file.FullName.Substring($basePath.Length + 1)
+ Write-Host -ForegroundColor DarkCyan "Uploading $path..."
+- $pom = (Get-ChildItem $file.Directory -Filter *.pom).FullName
++ $pom = (dir $file.Directory -Filter *.pom).FullName
+ [xml]$pomXml = Get-Content $pom
+ $groupId = if ($pomXml.project.groupId) { $pomXml.project.groupId } else { $pomXml.project.parent.groupId }
+ $uri = $groupId.Replace(".", "/") + $path.Substring($groupId.Length).Replace("\", "/")
+- Invoke-WebRequest -InFile $file.FullName -Method Put "$repoUri/$uri" -Headers $headers -UseBasicParsing | Out-Null
++ $response = Invoke-WebRequest -InFile $file.FullName -Method Put "$repoUri/$uri" -Headers $headers
+ }
+
+ Write-Host -ForegroundColor Cyan "Build successfully uploaded. Go to https://oss.sonatype.org and promote the release."
+diff --git a/build/typescript-generator-gradle-plugin-publisher/build.gradle b/build/typescript-generator-gradle-plugin-publisher/build.gradle
+deleted file mode 100644
+index 6a61f6f1f..000000000
+--- a/build/typescript-generator-gradle-plugin-publisher/build.gradle
++++ /dev/null
+@@ -1,40 +0,0 @@
+-
+-plugins {
+- id 'com.gradle.plugin-publish' version '1.0.0'
+- id 'java-library'
+-}
+-
+-group = 'cz.habarta.typescript-generator'
+-
+-repositories {
+- mavenCentral()
+- mavenLocal()
+-}
+-
+-dependencies {
+- api "cz.habarta.typescript-generator:typescript-generator-gradle-plugin:${version}"
+- api "cz.habarta.typescript-generator:typescript-generator-spring:${version}"
+-}
+-
+-task printVersion {
+- doLast {
+- println "Releasing 'cz.habarta.typescript-generator' Gradle plugin version ${project.version}..."
+- }
+-}
+-
+-pluginBundle {
+- website = 'https://github.com/vojtechhabarta/typescript-generator'
+- vcsUrl = 'https://github.com/vojtechhabarta/typescript-generator'
+- tags = ['java', 'kotlin', 'json', 'typescript', 'typescript-generator', 'jackson', 'jakarta', 'rest', 'client', 'spring']
+-}
+-
+-gradlePlugin {
+- plugins {
+- typescriptGeneratorPlugin {
+- id = 'cz.habarta.typescript-generator'
+- displayName = 'Typescript-generator Gradle plugin'
+- description = 'Generates TypeScript from Java - JSON declarations, REST service client'
+- implementationClass = 'cz.habarta.typescript.generator.gradle.TypeScriptGeneratorPublishedPlugin'
+- }
+- }
+-}
+diff --git a/build/typescript-generator-gradle-plugin-publisher/src/main/java/cz/habarta/typescript/generator/gradle/TypeScriptGeneratorPublishedPlugin.java b/build/typescript-generator-gradle-plugin-publisher/src/main/java/cz/habarta/typescript/generator/gradle/TypeScriptGeneratorPublishedPlugin.java
+deleted file mode 100644
+index 7b5b93f7b..000000000
+--- a/build/typescript-generator-gradle-plugin-publisher/src/main/java/cz/habarta/typescript/generator/gradle/TypeScriptGeneratorPublishedPlugin.java
++++ /dev/null
+@@ -1,5 +0,0 @@
+-
+-package cz.habarta.typescript.generator.gradle;
+-
+-public class TypeScriptGeneratorPublishedPlugin extends TypeScriptGeneratorPlugin {
+-}
+diff --git a/checkstyle.xml b/checkstyle.xml
+deleted file mode 100644
+index e784bb838..000000000
+--- a/checkstyle.xml
++++ /dev/null
+@@ -1,30 +0,0 @@
+-
+-
+-
+-
+-
+-
+-
+-
+-
+-
+-
+-
+-
+-
+-
+-
+-
+-
+-
+-
+-
+-
+-
+-
+-
+-
+-
+-
+diff --git a/pom.xml b/pom.xml
+index a69f86d5b..b028e643e 100644
+--- a/pom.xml
++++ b/pom.xml
+@@ -5,7 +5,7 @@
+ cz.habarta.typescript-generator
+ typescript-generator
+ pom
+- 3.2-SNAPSHOT
++ 1.12-SNAPSHOT
+ typescript-generator
+ Generates TypeScript declaration file from specified java classes.
+ https://github.com/vojtechhabarta/typescript-generator
+@@ -14,7 +14,6 @@
+ typescript-generator-core
+ typescript-generator-maven-plugin
+ typescript-generator-gradle-plugin
+- typescript-generator-spring
+
+
+
+@@ -54,205 +53,59 @@
+
+
+ UTF-8
+- 11
+- 1.9.10
+ github
+
+
+-
+-
+-
+- org.glassfish.jaxb
+- jaxb-bom
+- 4.0.3
+- pom
+- import
+-
+-
+- jakarta.xml.bind
+- jakarta.xml.bind-api
+- 4.0.0
+-
+-
+- javax.xml.bind
+- jaxb-api
+- 2.3.1
+-
+-
+- jakarta.ws.rs
+- jakarta.ws.rs-api
+- 3.1.0
+-
+-
+- javax.ws.rs
+- javax.ws.rs-api
+- 2.1.1
+-
+-
+- jakarta.json
+- jakarta.json-api
+- 2.1.2
+-
+-
+- javax.json
+- javax.json-api
+- 1.1.4
+-
+-
+- jakarta.json.bind
+- jakarta.json.bind-api
+- 3.0.0
+-
+-
+- javax.json.bind
+- javax.json.bind-api
+- 1.0
+-
+-
+- jakarta.activation
+- jakarta.activation-api
+- 2.1.2
+-
+-
+- javax.activation
+- javax.activation-api
+- 1.2.0
+-
+-
+- org.junit
+- junit-bom
+- 5.10.0
+- pom
+- import
+-
+-
+- com.fasterxml.jackson
+- jackson-bom
+- 2.15.2
+- pom
+- import
+-
+-
+- org.glassfish.jersey
+- jersey-bom
+- 3.1.3
+- pom
+- import
+-
+-
+- org.jetbrains.kotlin
+- kotlin-bom
+- ${kotlin.version}
+- pom
+- import
+-
+-
+- org.springframework.boot
+- spring-boot-dependencies
+- 2.7.3
+- pom
+- import
+-
+-
+-
+-
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+- 3.11.0
++ 3.2
+
+- ${java.version}
+- ${java.version}
++ 1.7
++ 1.7
+ true
+ true
+-
+- -Xlint
+- -parameters
+-
+-
+-
+-
+- org.jetbrains.kotlin
+- kotlin-maven-plugin
+- ${kotlin.version}
+-
+-
+-
+-
+-
+- ${java.version}
++ -Xlint
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+- 3.1.2
++ 2.18.1
+
+ -Dfile.encoding=UTF-8
+- false
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+- 3.3.0
++ 2.5
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+- 3.3.0
++ 2.4
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+- 3.5.0
+-
+-
+- org.apache.maven.plugins
+- maven-checkstyle-plugin
+- 3.3.0
+-
+-
+- com.puppycrawl.tools
+- checkstyle
+- 9.3
+-
+-
+-
+-
+- ${project.build.sourceDirectory}
+- ${project.build.testSourceDirectory}
+-
+- ../checkstyle.xml
+-
++ 2.10.1
+
+
+ org.apache.maven.plugins
+ maven-site-plugin
+- 3.12.1
++ 3.4
+
+ true
+
+
+
+- org.apache.maven.doxia
++ net.ju-n.maven.doxia
+ doxia-module-markdown
+- 1.12.0
++ 1.0.0
+
+
+
+@@ -287,7 +140,7 @@
+
+ com.github.github
+ site-maven-plugin
+- 0.12
++ 0.11
+
+
+ site
+@@ -303,16 +156,6 @@
+
+
+
+-
+- org.apache.maven.plugins
+- maven-project-info-reports-plugin
+- 3.4.5
+-
+-
+- org.apache.maven.plugins
+- maven-plugin-plugin
+- 3.9.0
+-
+
+
+
+@@ -357,7 +200,7 @@
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+- 3.1.1
++ 2.8.2
+
+
+ local-deploy
+@@ -402,10 +245,12 @@
+
+ org.apache.maven.plugins
+ maven-project-info-reports-plugin
++ 2.8
+
+
+ org.apache.maven.plugins
+ maven-plugin-plugin
++ 3.4
+
+
+
+diff --git a/sample-gradle-spring/build.gradle b/sample-gradle-spring/build.gradle
+deleted file mode 100644
+index f5ba194a4..000000000
+--- a/sample-gradle-spring/build.gradle
++++ /dev/null
+@@ -1,30 +0,0 @@
+-
+-plugins {
+- id 'java'
+- id 'cz.habarta.typescript-generator' version 'FILL_VERSION'
+-}
+-
+-version = '3.0'
+-sourceCompatibility = 11
+-targetCompatibility = 11
+-
+-repositories {
+- mavenCentral()
+-}
+-
+-dependencies {
+- implementation 'org.springframework.boot:spring-boot-starter-web:2.7.4'
+-}
+-
+-generateTypeScript {
+- classes = [
+- 'cz.habarta.typescript.generator.sample.spring.SpringTestApplication'
+- ]
+- outputFileType = 'implementationFile'
+- jsonLibrary = 'jackson2'
+- outputKind = 'module'
+- scanSpringApplication = true
+- generateSpringApplicationClient = true
+-}
+-
+-build.dependsOn generateTypeScript
+diff --git a/sample-gradle-spring/src/main/java/cz/habarta/typescript/generator/sample/spring/SpringTestApplication.java b/sample-gradle-spring/src/main/java/cz/habarta/typescript/generator/sample/spring/SpringTestApplication.java
+deleted file mode 100644
+index 3cf2e7360..000000000
+--- a/sample-gradle-spring/src/main/java/cz/habarta/typescript/generator/sample/spring/SpringTestApplication.java
++++ /dev/null
+@@ -1,51 +0,0 @@
+-
+-package cz.habarta.typescript.generator.sample.spring;
+-
+-import java.util.concurrent.atomic.AtomicLong;
+-import org.springframework.boot.SpringApplication;
+-import org.springframework.boot.autoconfigure.SpringBootApplication;
+-import org.springframework.web.bind.annotation.RequestMapping;
+-import org.springframework.web.bind.annotation.RequestParam;
+-import org.springframework.web.bind.annotation.RestController;
+-
+-
+-@SpringBootApplication
+-public class SpringTestApplication {
+-
+- public static void main(String[] args) {
+- SpringApplication.run(SpringTestApplication.class, args);
+- }
+-
+- @RestController
+- public static class GreetingController {
+-
+- private static final String template = "Hello, %s!";
+- private final AtomicLong counter = new AtomicLong();
+-
+- @RequestMapping("/greeting")
+- public Greeting greeting(@RequestParam(value="name", defaultValue="World") String name) {
+- return new Greeting(counter.incrementAndGet(), String.format(template, name));
+- }
+-
+- }
+-
+- public static class Greeting {
+-
+- private final long id;
+- private final String content;
+-
+- public Greeting(long id, String content) {
+- this.id = id;
+- this.content = content;
+- }
+-
+- public long getId() {
+- return id;
+- }
+-
+- public String getContent() {
+- return content;
+- }
+- }
+-
+-}
+diff --git a/sample-gradle/build.gradle b/sample-gradle/build.gradle
+index 0ecd1593e..7bc133102 100644
+--- a/sample-gradle/build.gradle
++++ b/sample-gradle/build.gradle
+@@ -1,67 +1,38 @@
+
+-buildscript {
+- // /*dev*/ repositories {
+- // /*dev*/ mavenLocal()
+- // /*dev*/ }
+- dependencies {
+- classpath 'com.fasterxml.jackson.module:jackson-module-scala_2.13:2.14.2'
+- // /*dev*/ classpath 'cz.habarta.typescript-generator:typescript-generator-gradle-plugin:FILL_VERSION-SNAPSHOT'
+- }
+-}
++apply plugin: 'java'
++apply plugin: 'cz.habarta.typescript-generator'
+
+-plugins {
+- id 'java'
+- id 'groovy'
+- id "org.jetbrains.kotlin.jvm" version "1.8.10"
+- id 'scala'
+- /*prod*/ id 'cz.habarta.typescript-generator' version 'FILL_VERSION'
+-}
+-
+-// /*dev*/ apply plugin: 'cz.habarta.typescript-generator'
+
+-version = '3.0'
+-sourceCompatibility = 11
+-targetCompatibility = 11
++version = '1.0'
++sourceCompatibility = 1.7
++targetCompatibility = 1.7
+
+ repositories {
+ mavenCentral()
+ }
+-
+ dependencies {
+- implementation 'com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:2.14.2'
+- implementation 'org.codehaus.groovy:groovy-all:3.0.16'
+- implementation 'org.jetbrains.kotlin:kotlin-stdlib:1.8.10'
+- implementation 'org.scala-lang:scala-library:2.13.10'
+- implementation 'com.fasterxml.jackson.module:jackson-module-kotlin:2.14.2'
++ compile group: 'com.fasterxml.jackson.jaxrs', name: 'jackson-jaxrs-json-provider', version: '2.6.3'
+ }
+
+-tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile) {
+- kotlinOptions {
+- jvmTarget = '11'
++buildscript {
++ repositories {
++// mavenCentral()
++ mavenLocal()
++ }
++ dependencies {
++ classpath group: 'cz.habarta.typescript-generator', name: 'typescript-generator-gradle-plugin', version: '1.7-SNAPSHOT'
+ }
+ }
+
+ generateTypeScript {
++ outputFile = 'build/sample.d.ts'
+ classes = [
+- 'cz.habarta.typescript.generator.sample.Person',
+- 'cz.habarta.typescript.generator.sample.PersonGroovy',
+- 'cz.habarta.typescript.generator.sample.PersonKt',
+- 'cz.habarta.typescript.generator.sample.PersonScala',
++ 'cz.habarta.typescript.generator.sample.Person'
+ ]
+ jsonLibrary = 'jackson2'
+- outputKind = 'module'
+- excludeClasses = [
+- 'groovy.lang.GroovyObject',
+- 'groovy.lang.MetaClass',
+- 'java.io.Serializable',
+- 'scala.Equals',
+- 'scala.Product',
+- 'scala.Serializable',
+- ]
+- jackson2Modules = [
+- 'com.fasterxml.jackson.module.scala.DefaultScalaModule',
+- 'com.fasterxml.jackson.module.kotlin.KotlinModule',
+- ]
++ outputKind = 'global'
++ namespace = 'GradleSample';
++// declarePropertiesAsOptional = false
++// removeTypeNameSuffix = 'Json'
++// mapDate = 'asNumber'
+ }
+-
+-build.dependsOn generateTypeScript
+diff --git a/sample-gradle/src/main/groovy/cz/habarta/typescript/generator/sample/PersonGroovy.groovy b/sample-gradle/src/main/groovy/cz/habarta/typescript/generator/sample/PersonGroovy.groovy
+deleted file mode 100644
+index b07d16a8f..000000000
+--- a/sample-gradle/src/main/groovy/cz/habarta/typescript/generator/sample/PersonGroovy.groovy
++++ /dev/null
+@@ -1,13 +0,0 @@
+-
+-package cz.habarta.typescript.generator.sample
+-
+-import com.fasterxml.jackson.annotation.JsonIgnoreProperties
+-
+-@JsonIgnoreProperties("metaClass")
+-class PersonGroovy {
+- public String name
+- public int age
+- public boolean hasChildren
+- public List tags
+- public Map emails
+-}
+diff --git a/sample-gradle/src/main/java/cz/habarta/typescript/generator/sample/Person.java b/sample-gradle/src/main/java/cz/habarta/typescript/generator/sample/Person.java
+index 7d8f61d2e..52cefc546 100644
+--- a/sample-gradle/src/main/java/cz/habarta/typescript/generator/sample/Person.java
++++ b/sample-gradle/src/main/java/cz/habarta/typescript/generator/sample/Person.java
+@@ -3,10 +3,13 @@ package cz.habarta.typescript.generator.sample;
+
+ import java.util.*;
+
++
+ public class Person {
++
+ public String name;
+ public int age;
+ public boolean hasChildren;
+ public List tags;
+ public Map emails;
++
+ }
+diff --git a/sample-gradle/src/main/kotlin/cz/habarta/typescript/generator/sample/PersonKt.kt b/sample-gradle/src/main/kotlin/cz/habarta/typescript/generator/sample/PersonKt.kt
+deleted file mode 100644
+index 93d20c7e2..000000000
+--- a/sample-gradle/src/main/kotlin/cz/habarta/typescript/generator/sample/PersonKt.kt
++++ /dev/null
+@@ -1,10 +0,0 @@
+-
+-package cz.habarta.typescript.generator.sample
+-
+-data class PersonKt (
+- val name: String,
+- val age: Int,
+- val hasChildren: Boolean,
+- val tags: List,
+- val emails: Map
+-)
+diff --git a/sample-gradle/src/main/scala/cz/habarta/typescript/generator/sample/PersonScala.scala b/sample-gradle/src/main/scala/cz/habarta/typescript/generator/sample/PersonScala.scala
+deleted file mode 100644
+index 75310d957..000000000
+--- a/sample-gradle/src/main/scala/cz/habarta/typescript/generator/sample/PersonScala.scala
++++ /dev/null
+@@ -1,10 +0,0 @@
+-
+-package cz.habarta.typescript.generator.sample;
+-
+-case class PersonScala (
+- name: String,
+- age: Int,
+- hasChildren: Boolean,
+- tags: java.util.List[String],
+- emails: java.util.Map[String, String]
+-)
+diff --git a/sample-maven-spring/pom.xml b/sample-maven-spring/pom.xml
+deleted file mode 100644
+index bc921243c..000000000
+--- a/sample-maven-spring/pom.xml
++++ /dev/null
+@@ -1,73 +0,0 @@
+-
+-
+- 4.0.0
+-
+- cz.habarta.typescript-generator
+- sample-maven-spring
+- 3.0-SNAPSHOT
+- jar
+- sample-maven-spring
+-
+-
+- FILL_VERSION
+-
+-
+-
+-
+- org.springframework.boot
+- spring-boot-starter-web
+- 2.7.4
+-
+-
+-
+-
+-
+-
+- org.apache.maven.plugins
+- maven-compiler-plugin
+- 3.10.1
+-
+- 11
+- 11
+-
+- -parameters
+-
+-
+-
+-
+- cz.habarta.typescript-generator
+- typescript-generator-maven-plugin
+- ${typescript-generator.version}
+-
+-
+- generate
+-
+- generate
+-
+- process-classes
+-
+-
+-
+- jackson2
+- implementationFile
+- module
+-
+- cz.habarta.typescript.generator.sample.spring.SpringTestApplication
+-
+- true
+- true
+-
+- cz.habarta.typescript.generator.ext.AxiosClientExtension
+-
+-
+-
+-
+- cz.habarta.typescript-generator
+- typescript-generator-spring
+- ${typescript-generator.version}
+-
+-
+-
+-
+-
+-
+diff --git a/sample-maven-spring/src/main/java/cz/habarta/typescript/generator/sample/spring/SpringTestApplication.java b/sample-maven-spring/src/main/java/cz/habarta/typescript/generator/sample/spring/SpringTestApplication.java
+deleted file mode 100644
+index 3cf2e7360..000000000
+--- a/sample-maven-spring/src/main/java/cz/habarta/typescript/generator/sample/spring/SpringTestApplication.java
++++ /dev/null
+@@ -1,51 +0,0 @@
+-
+-package cz.habarta.typescript.generator.sample.spring;
+-
+-import java.util.concurrent.atomic.AtomicLong;
+-import org.springframework.boot.SpringApplication;
+-import org.springframework.boot.autoconfigure.SpringBootApplication;
+-import org.springframework.web.bind.annotation.RequestMapping;
+-import org.springframework.web.bind.annotation.RequestParam;
+-import org.springframework.web.bind.annotation.RestController;
+-
+-
+-@SpringBootApplication
+-public class SpringTestApplication {
+-
+- public static void main(String[] args) {
+- SpringApplication.run(SpringTestApplication.class, args);
+- }
+-
+- @RestController
+- public static class GreetingController {
+-
+- private static final String template = "Hello, %s!";
+- private final AtomicLong counter = new AtomicLong();
+-
+- @RequestMapping("/greeting")
+- public Greeting greeting(@RequestParam(value="name", defaultValue="World") String name) {
+- return new Greeting(counter.incrementAndGet(), String.format(template, name));
+- }
+-
+- }
+-
+- public static class Greeting {
+-
+- private final long id;
+- private final String content;
+-
+- public Greeting(long id, String content) {
+- this.id = id;
+- this.content = content;
+- }
+-
+- public long getId() {
+- return id;
+- }
+-
+- public String getContent() {
+- return content;
+- }
+- }
+-
+-}
+diff --git a/sample-maven/pom.xml b/sample-maven/pom.xml
+index e28e18f7b..2cd4bb2af 100644
+--- a/sample-maven/pom.xml
++++ b/sample-maven/pom.xml
+@@ -4,7 +4,7 @@
+
+ cz.habarta.typescript-generator
+ sample-maven
+- 3.0-SNAPSHOT
++ 1.0-SNAPSHOT
+ jar
+ sample-maven
+
+@@ -12,28 +12,16 @@
+
+ com.fasterxml.jackson.jaxrs
+ jackson-jaxrs-json-provider
+- 2.13.4
++ 2.7.4
+
+
+
+
+
+-
+- org.apache.maven.plugins
+- maven-compiler-plugin
+- 3.10.1
+-
+- 11
+- 11
+-
+- -parameters
+-
+-
+-
+
+ cz.habarta.typescript-generator
+ typescript-generator-maven-plugin
+- FILL_VERSION
++ 1.10-SNAPSHOT
+
+
+ generate
+@@ -41,15 +29,16 @@
+ generate
+
+ process-classes
++
++ jackson2
++
++ cz.habarta.typescript.generator.sample.Person
++
++ target/sample.d.ts
++ module
++
+
+
+-
+- jackson2
+-
+- cz.habarta.typescript.generator.sample.Person
+-
+- module
+-
+
+
+
+diff --git a/sample-maven/src/main/java/cz/habarta/typescript/generator/sample/Person.java b/sample-maven/src/main/java/cz/habarta/typescript/generator/sample/Person.java
+index 7d8f61d2e..52cefc546 100644
+--- a/sample-maven/src/main/java/cz/habarta/typescript/generator/sample/Person.java
++++ b/sample-maven/src/main/java/cz/habarta/typescript/generator/sample/Person.java
+@@ -3,10 +3,13 @@ package cz.habarta.typescript.generator.sample;
+
+ import java.util.*;
+
++
+ public class Person {
++
+ public String name;
+ public int age;
+ public boolean hasChildren;
+ public List tags;
+ public Map emails;
++
+ }
+diff --git a/tslint.json b/tslint.json
+deleted file mode 100644
+index 9e26dfeeb..000000000
+--- a/tslint.json
++++ /dev/null
+@@ -1 +0,0 @@
+-{}
+\ No newline at end of file
+diff --git a/typescript-generator-core/package-lock.json b/typescript-generator-core/package-lock.json
+deleted file mode 100644
+index 2b1f673c2..000000000
+--- a/typescript-generator-core/package-lock.json
++++ /dev/null
+@@ -1,20 +0,0 @@
+-{
+- "name": "typescript-generator-core",
+- "version": "1.0.0",
+- "lockfileVersion": 1,
+- "requires": true,
+- "dependencies": {
+- "@types/node": {
+- "version": "14.17.20",
+- "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.20.tgz",
+- "integrity": "sha512-gI5Sl30tmhXsqkNvopFydP7ASc4c2cLfGNQrVKN3X90ADFWFsPEsotm/8JHSUJQKTHbwowAHtcJPeyVhtKv0TQ==",
+- "dev": true
+- },
+- "typescript": {
+- "version": "4.4.3",
+- "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz",
+- "integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==",
+- "dev": true
+- }
+- }
+-}
+diff --git a/typescript-generator-core/package.json b/typescript-generator-core/package.json
+deleted file mode 100644
+index 46db5c63f..000000000
+--- a/typescript-generator-core/package.json
++++ /dev/null
+@@ -1,15 +0,0 @@
+-{
+- "name": "typescript-generator-core",
+- "version": "1.0.0",
+- "description": "Support for tests",
+- "scripts": {
+- "test": "npm run ModulesAndNamespacesTest && npm run JsonDeserializationTest",
+- "ModulesAndNamespacesTest": "tsc -p src/test/ts/tsconfig.json",
+- "JsonDeserializationTest": "tsc -p src/test/ts/JsonDeserializationTest/tsconfig.json && node src/test/ts/JsonDeserializationTest/JsonDeserializationTest-expected-test.js"
+- },
+- "license": "ISC",
+- "devDependencies": {
+- "typescript": "^4.3.2",
+- "@types/node": "^14.17.0"
+- }
+-}
+diff --git a/typescript-generator-core/pom.xml b/typescript-generator-core/pom.xml
+index 32546e091..5b0d50a49 100644
+--- a/typescript-generator-core/pom.xml
++++ b/typescript-generator-core/pom.xml
+@@ -5,116 +5,40 @@
+
+ cz.habarta.typescript-generator
+ typescript-generator
+- 3.2-SNAPSHOT
++ 1.12-SNAPSHOT
+
+
+ typescript-generator-core
+ jar
+ typescript-generator-core
+
+-
+- 22.3.1
+- 0.10.3
+-
+-
+
+
+
+- com.fasterxml.jackson.core
+- jackson-databind
+-
+-
+- com.fasterxml.jackson.module
+- jackson-module-jakarta-xmlbind-annotations
+-
+-
+- com.fasterxml.jackson.module
+- jackson-module-jaxb-annotations
+-
+-
+- org.glassfish.jaxb
+- jaxb-runtime
++ org.codehaus.jackson
++ jackson-mapper-asl
++ 1.9.13
+
+
+- javax.xml.bind
+- jaxb-api
++ com.fasterxml.jackson.jaxrs
++ jackson-jaxrs-json-provider
++ 2.6.3
+
+
+ javax.ws.rs
+ javax.ws.rs-api
++ 2.0.1
+
+
+- jakarta.ws.rs
+- jakarta.ws.rs-api
+-
+-
+- io.github.classgraph
+- classgraph
+- 4.8.162
+-
+-
+- com.google.code.gson
+- gson
+- 2.10.1
+-
+-
+- org.jetbrains.kotlin
+- kotlin-stdlib
+-
+-
+- org.jetbrains
+- annotations
+-
+-
+-
+-
+-
+-
+- org.jetbrains
+- annotations
+- 24.0.1
+-
+-
+- org.jetbrains.kotlin
+- kotlin-reflect
+-
+-
+- javax.json
+- javax.json-api
+-
+-
+- jakarta.json
+- jakarta.json-api
+-
+-
+- javax.json.bind
+- javax.json.bind-api
+-
+-
+- jakarta.json.bind
+- jakarta.json.bind-api
+-
+-
+- org.graalvm.js
+- js
+- ${graalvm.version}
+- runtime
+-
+-
+- org.graalvm.js
+- js-scriptengine
+- ${graalvm.version}
++ io.github.lukehutch
++ fast-classpath-scanner
++ 1.9.19
+
+
+
+- org.junit.jupiter
+- junit-jupiter
+- test
+-
+-
+- org.immutables
+- value
+- 2.9.3
++ junit
++ junit
++ 4.11
+ test
+
+
+@@ -123,116 +47,14 @@
+ 3.0.1u2
+ test
+
+-
+- com.fasterxml.jackson.datatype
+- jackson-datatype-jdk8
+- test
+-
+-
+- com.fasterxml.jackson.datatype
+- jackson-datatype-jsr310
+- test
+-
+-
+- org.glassfish.jersey.containers
+- jersey-container-jdk-http
+- test
+-
+-
+- org.glassfish.jersey.media
+- jersey-media-json-binding
+- test
+-
+-
+- org.glassfish.jersey.media
+- jersey-media-json-jackson
+- test
+-
+-
+- org.glassfish.jersey.inject
+- jersey-hk2
+- test
+-
+-
+- io.swagger
+- swagger-annotations
+- 1.6.11
+- test
+-
+-
+- io.swagger.core.v3
+- swagger-annotations
+- 2.2.15
+- test
+-
+-
+- org.checkerframework
+- checker-qual
+- 3.38.0
+- test
+-
+-
+-
+- com.google.guava
+- guava
+- 32.1.2-jre
+- test
+-
+-
+- com.fasterxml.jackson.datatype
+- jackson-datatype-guava
+- test
+-
+-
+- joda-time
+- joda-time
+- 2.12.5
+- test
+-
+-
+- com.fasterxml.jackson.datatype
+- jackson-datatype-joda
+- test
+-
+-
+- io.vavr
+- vavr
+- ${vavr.version}
+- test
+-
+-
+- io.vavr
+- vavr-jackson
+- ${vavr.version}
+- test
+-
+-
+
+
+
+
+
+- org.jetbrains.kotlin
+- kotlin-maven-plugin
+-
+-
+- test-compile
+- test-compile
+-
+- test-compile
+-
+-
+-
+-
+-
+- com.evolvedbinary.maven.jvnet
+- jaxb30-maven-plugin
+- 0.15.0
++ org.jvnet.jaxb2.maven2
++ maven-jaxb2-plugin
++ 0.13.1
+
+
+ generate
+@@ -249,211 +71,29 @@
+
+
+ org.apache.maven.plugins
+- maven-antrun-plugin
+- 3.1.0
++ maven-javadoc-plugin
++ 2.10.3
+
+
+- jaxrs-v2-test
+- generate-test-sources
+-
+- run
+-
+-
+-
+-
+-
+-
+-
+-
+-
+-
+-
+-
+-
+- jaxrs-v2-test-delete
+- verify
++ test-xml-doclet
++ process-test-classes
+
+- run
++ test-javadoc
+
+
+-
+-
+-
+-
+-
+-
+-
+-
+-
+- org.apache.maven.plugins
+- maven-surefire-plugin
+-
+-
+- default-test
+-
+- typescript2java
++ com.github.markusbernhardt.xmldoclet.XmlDoclet
++ -d ${project.build.directory} -filename test-javadoc.xml
++ false
++
++ com.github.markusbernhardt
++ xml-doclet
++ 1.0.5
++
+
+
+
+
+-
+- org.apache.maven.plugins
+- maven-jar-plugin
+-
+-
+-
+- test-jar
+-
+-
+-
+-
+-
+- org.apache.maven.plugins
+- maven-checkstyle-plugin
+-
+-
+- check
+- verify
+-
+- check
+-
+-
+-
+-
+
+
+
+-
+-
+- test-javadoc
+-
+-
+-
+- org.apache.maven.plugins
+- maven-javadoc-plugin
+- 3.5.0
+-
+-
+- test-xml-doclet
+- process-test-classes
+-
+- test-javadoc
+-
+-
+- com.github.markusbernhardt.xmldoclet.XmlDoclet
+- -d ${project.basedir}/src/test/javadoc -filename test-javadoc.xml
+- false
+-
+-
+- com.github.markusbernhardt
+- xml-doclet
+- 1.0.5
+-
+-
+- jakarta.xml.bind
+- jakarta.xml.bind-api
+- 2.3.3
+-
+-
+- com.sun.xml.bind
+- jaxb-impl
+- 2.3.6
+-
+-
+- org.slf4j
+- slf4j-api
+- 1.7.36
+-
+-
+- org.slf4j
+- slf4j-simple
+- 1.7.36
+-
+-
+-
+-
+-
+-
+-
+-
+-
+-
+- run-npm-test
+-
+-
+-
+- com.github.eirslett
+- frontend-maven-plugin
+- 1.13.4
+-
+-
+- install node and npm
+-
+- install-node-and-npm
+-
+- generate-resources
+-
+-
+- npm install
+-
+- npm
+-
+- generate-resources
+-
+- install
+-
+-
+-
+- npm test
+-
+- npm
+-
+- test
+-
+- test
+-
+-
+-
+-
+- v14.17.0
+-
+-
+-
+- org.apache.maven.plugins
+- maven-surefire-plugin
+-
+-
+- test-typescript2java
+- test
+-
+- test
+-
+-
+-
+- typescript2java
+-
+-
+-
+-
+-
+-
+-
+-
+-
+
+diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/ClassMapping.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/ClassMapping.java
+deleted file mode 100644
+index f2ee7fe84..000000000
+--- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/ClassMapping.java
++++ /dev/null
+@@ -1,7 +0,0 @@
+-
+-package cz.habarta.typescript.generator;
+-
+-
+-public enum ClassMapping {
+- asInterfaces, asClasses;
+-}
+diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/CustomMappingTypeProcessor.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/CustomMappingTypeProcessor.java
+index b4cac62bd..2fe6e40c1 100644
+--- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/CustomMappingTypeProcessor.java
++++ b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/CustomMappingTypeProcessor.java
+@@ -1,69 +1,29 @@
+
+ package cz.habarta.typescript.generator;
+
+-import cz.habarta.typescript.generator.util.GenericsResolver;
+ import cz.habarta.typescript.generator.util.Utils;
+ import java.lang.reflect.Type;
+-import java.util.ArrayList;
+-import java.util.List;
+-import java.util.function.Function;
++import java.util.*;
+
+
+ public class CustomMappingTypeProcessor implements TypeProcessor {
+
+- private final List customMappings;
++ private final Map customMappings;
+
+- public CustomMappingTypeProcessor(List customMappings) {
++ public CustomMappingTypeProcessor(Map customMappings) {
+ this.customMappings = customMappings;
+ }
+
+ @Override
+ public Result processType(Type javaType, Context context) {
+ final Class> rawClass = Utils.getRawClassOrNull(javaType);
+- if (rawClass == null) {
+- return null;
+- }
+- final Settings.CustomTypeMapping mapping = customMappings.stream()
+- .filter(m -> m.matchSubclasses
+- ? m.rawClass.isAssignableFrom(rawClass)
+- : m.rawClass.equals(rawClass)
+- )
+- .findFirst()
+- .orElse(null);
+- if (mapping == null) {
+- return null;
+- }
+-
+- final List resolvedTypeParameters = GenericsResolver.resolveBaseGenericVariables(mapping.rawClass, javaType);
+- final List> discoveredClasses = new ArrayList<>();
+- final Function processGenericParameter = index -> {
+- final Type typeArgument = resolvedTypeParameters.get(index);
+- final TypeProcessor.Result typeArgumentResult = context.processType(typeArgument);
+- discoveredClasses.addAll(typeArgumentResult.getDiscoveredClasses());
+- return typeArgumentResult.getTsType();
+- };
+- if (mapping.tsType.typeParameters != null) {
+- final List tsTypeArguments = new ArrayList<>();
+- for (String typeParameter : mapping.tsType.typeParameters) {
+- final TsType tsType;
+- final int index = mapping.javaType.indexOfTypeParameter(typeParameter);
+- if (index != -1) {
+- tsType = processGenericParameter.apply(index);
+- } else {
+- tsType = new TsType.VerbatimType(typeParameter);
+- }
+- tsTypeArguments.add(tsType);
+- }
+- return new Result(new TsType.GenericBasicType(mapping.tsType.rawName, tsTypeArguments), discoveredClasses);
+- } else {
+- final int index = mapping.javaType.indexOfTypeParameter(mapping.tsType.rawName);
+- if (index != -1) {
+- final TsType tsType = processGenericParameter.apply(index);
+- return new Result(tsType, discoveredClasses);
+- } else {
+- return new Result(new TsType.VerbatimType(mapping.tsType.rawName), discoveredClasses);
++ if (rawClass != null) {
++ final String tsTypeName = customMappings.get(rawClass.getName());
++ if (tsTypeName != null) {
++ return new Result(new TsType.BasicType(tsTypeName));
+ }
+ }
++ return null;
+ }
+
+ }
+diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/DataLibraryJson.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/DataLibraryJson.java
+deleted file mode 100644
+index 3ef9b8e3b..000000000
+--- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/DataLibraryJson.java
++++ /dev/null
+@@ -1,50 +0,0 @@
+-
+-package cz.habarta.typescript.generator;
+-
+-import com.fasterxml.jackson.annotation.JsonValue;
+-import java.util.List;
+-
+-
+-public class DataLibraryJson {
+-
+- public List classMappings;
+- public List typeAliases;
+-
+- public static class ClassMapping {
+- public String className;
+- public SemanticType semanticType;
+- public String customType;
+- }
+-
+- public enum SemanticType {
+- String("string"),
+- Number("number"),
+- Boolean("boolean"),
+- Date("date"),
+- Any("any"),
+- Void("void"),
+- List("list"),
+- Map("map"),
+- Optional("optional"),
+- Wrapper("wrapper"),
+- ;
+-
+- private final String name;
+-
+- private SemanticType(String name) {
+- this.name = name;
+- }
+-
+- @JsonValue
+- public String getName() {
+- return name;
+- }
+-
+- }
+-
+- public static class TypeAlias {
+- public String name;
+- public String definition;
+- }
+-
+-}
+diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/DefaultTypeProcessor.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/DefaultTypeProcessor.java
+index b3285a3f9..baac9c614 100644
+--- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/DefaultTypeProcessor.java
++++ b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/DefaultTypeProcessor.java
+@@ -1,151 +1,50 @@
+
+ package cz.habarta.typescript.generator;
+
+-import cz.habarta.typescript.generator.compiler.Symbol;
+-import cz.habarta.typescript.generator.type.JTypeWithNullability;
+-import cz.habarta.typescript.generator.type.JUnionType;
+-import cz.habarta.typescript.generator.util.GenericsResolver;
+-import cz.habarta.typescript.generator.util.Utils;
+-import java.lang.reflect.GenericArrayType;
+-import java.lang.reflect.Method;
+-import java.lang.reflect.ParameterizedType;
+-import java.lang.reflect.Type;
+-import java.lang.reflect.TypeVariable;
+-import java.lang.reflect.WildcardType;
+-import java.time.temporal.Temporal;
+-import java.util.ArrayList;
+-import java.util.Arrays;
+-import java.util.Calendar;
+-import java.util.Collection;
+-import java.util.Date;
+-import java.util.List;
+-import java.util.Map;
+-import java.util.Objects;
+-import java.util.Optional;
+-import java.util.OptionalDouble;
+-import java.util.OptionalInt;
+-import java.util.OptionalLong;
+-import java.util.UUID;
+-import java.util.stream.Collectors;
++import java.lang.reflect.*;
++import java.math.*;
++import java.util.*;
+
+
+ public class DefaultTypeProcessor implements TypeProcessor {
+
+- private final LoadedDataLibraries known;
+-
+- public DefaultTypeProcessor() {
+- this(null);
+- }
+-
+- public DefaultTypeProcessor(LoadedDataLibraries dataLibraries) {
+- this.known = LoadedDataLibraries.join(getKnownClasses(), dataLibraries);
+- }
+-
+- private static boolean isAssignableFrom(List> classes, Class> cls) {
+- return assignableFrom(classes, cls).isPresent();
+- }
+-
+- private static Optional> assignableFrom(List> classes, Class> cls) {
+- return classes.stream().filter(c -> c.isAssignableFrom(cls)).findFirst();
+- }
+-
+ @Override
+ public Result processType(Type javaType, Context context) {
+- if (Objects.equals(javaType, Object.class)) {
+- return new Result(TsType.Any);
+- }
+- if (javaType instanceof Class) {
+- final Class> javaClass = (Class>) javaType;
+- if (isAssignableFrom(known.stringClasses, javaClass)) {
+- return new Result(TsType.String);
+- }
+- if (isAssignableFrom(known.numberClasses, javaClass)) {
+- return new Result(TsType.Number);
+- }
+- if (isAssignableFrom(known.booleanClasses, javaClass)) {
+- return new Result(TsType.Boolean);
+- }
+- if (isAssignableFrom(known.dateClasses, javaClass)) {
+- return new Result(TsType.Date);
+- }
+- if (isAssignableFrom(known.voidClasses, javaClass)) {
+- return new Result(TsType.Void);
+- }
+- }
+- if (javaType instanceof Class) {
+- final Class> javaClass = (Class>) javaType;
+- final Symbol importedSymbol = context.getSymbolIfImported(javaClass);
+- if (importedSymbol != null) {
+- return new Result(new TsType.ReferenceType(importedSymbol));
+- }
+- }
++ if (KnownTypes.containsKey(javaType)) return new Result(KnownTypes.get(javaType));
+ if (javaType instanceof Class) {
+ final Class> javaClass = (Class>) javaType;
+- if (isAssignableFrom(known.anyClasses, javaClass)) {
+- return new Result(TsType.Any);
+- }
+ if (javaClass.isArray()) {
+- final Result result = context.processTypeInsideCollection(javaClass.getComponentType());
++ final Result result = context.processType(javaClass.getComponentType());
+ return new Result(new TsType.BasicArrayType(result.getTsType()), result.getDiscoveredClasses());
+ }
+ if (javaClass.isEnum()) {
+ return new Result(new TsType.EnumReferenceType(context.getSymbol(javaClass)), javaClass);
+ }
+- // list, map, optional, wrapper
+- final Result knownGenericTypeResult = processKnownGenericType(javaClass, javaClass, context);
+- if (knownGenericTypeResult != null) {
+- return knownGenericTypeResult;
+- }
+- if (OptionalInt.class.isAssignableFrom(javaClass) ||
+- OptionalLong.class.isAssignableFrom(javaClass) ||
+- OptionalDouble.class.isAssignableFrom(javaClass)) {
+- return new Result(TsType.Number.optional());
++ if (Collection.class.isAssignableFrom(javaClass)) {
++ return new Result(new TsType.BasicArrayType(TsType.Any));
+ }
+- // generic structural type used without type arguments
+- if (javaClass.getTypeParameters().length > 0) {
+- final List tsTypeArguments = new ArrayList<>();
+- for (int i = 0; i < javaClass.getTypeParameters().length; i++) {
+- tsTypeArguments.add(TsType.Any);
+- }
+- return new Result(new TsType.GenericReferenceType(context.getSymbol(javaClass), tsTypeArguments));
++ if (Map.class.isAssignableFrom(javaClass)) {
++ return new Result(new TsType.IndexedArrayType(TsType.String, TsType.Any));
+ }
+- // structural type
++ // consider it structural
+ return new Result(new TsType.ReferenceType(context.getSymbol(javaClass)), javaClass);
+ }
+ if (javaType instanceof ParameterizedType) {
+ final ParameterizedType parameterizedType = (ParameterizedType) javaType;
+ if (parameterizedType.getRawType() instanceof Class) {
+ final Class> javaClass = (Class>) parameterizedType.getRawType();
+- // list, map, optional, wrapper
+- final Result knownGenericTypeResult = processKnownGenericType(javaType, javaClass, context);
+- if (knownGenericTypeResult != null) {
+- return knownGenericTypeResult;
++ if (Collection.class.isAssignableFrom(javaClass)) {
++ final Result result = context.processType(parameterizedType.getActualTypeArguments()[0]);
++ return new Result(new TsType.BasicArrayType(result.getTsType()), result.getDiscoveredClasses());
+ }
+- // generic structural type
+- final List> discoveredClasses = new ArrayList<>();
+- discoveredClasses.add(javaClass);
+- final List tsTypeArguments = new ArrayList<>();
+- for (Type typeArgument : parameterizedType.getActualTypeArguments()) {
+- final TypeProcessor.Result typeArgumentResult = context.processType(typeArgument);
+- tsTypeArguments.add(typeArgumentResult.getTsType());
+- discoveredClasses.addAll(typeArgumentResult.getDiscoveredClasses());
++ if (Map.class.isAssignableFrom(javaClass)) {
++ final Result result = context.processType(parameterizedType.getActualTypeArguments()[1]);
++ return new Result(new TsType.IndexedArrayType(TsType.String, result.getTsType()), result.getDiscoveredClasses());
+ }
+- return new Result(new TsType.GenericReferenceType(context.getSymbol(javaClass), tsTypeArguments), discoveredClasses);
++ // consider it structural
++ return new Result(new TsType.ReferenceType(context.getSymbol(javaClass)), javaClass);
+ }
+ }
+- if (javaType instanceof GenericArrayType) {
+- final GenericArrayType genericArrayType = (GenericArrayType) javaType;
+- final Result result = context.processTypeInsideCollection(genericArrayType.getGenericComponentType());
+- return new Result(new TsType.BasicArrayType(result.getTsType()), result.getDiscoveredClasses());
+- }
+- if (javaType instanceof TypeVariable) {
+- final TypeVariable> typeVariable = (TypeVariable>) javaType;
+- if (typeVariable.getGenericDeclaration() instanceof Method) {
+- // example method: public T getData();
+- return context.processType(typeVariable.getBounds()[0]);
+- }
+- return new Result(new TsType.GenericVariableType(typeVariable.getName()));
+- }
+ if (javaType instanceof WildcardType) {
+ final WildcardType wildcardType = (WildcardType) javaType;
+ final Type[] upperBounds = wildcardType.getUpperBounds();
+@@ -153,91 +52,40 @@ public class DefaultTypeProcessor implements TypeProcessor {
+ ? context.processType(upperBounds[0])
+ : new Result(TsType.Any);
+ }
+- if (javaType instanceof JUnionType) {
+- final JUnionType unionType = (JUnionType) javaType;
+- final List results = unionType.getTypes().stream()
+- .map(type -> context.processType(type))
+- .collect(Collectors.toList());
+- return new Result(
+- new TsType.UnionType(results.stream()
+- .map(result -> result.getTsType())
+- .collect(Collectors.toList())),
+- results.stream()
+- .flatMap(result -> result.getDiscoveredClasses().stream())
+- .collect(Collectors.toList())
+- );
+- }
+- if (javaType instanceof JTypeWithNullability) {
+- final JTypeWithNullability typeWithNullability = (JTypeWithNullability) javaType;
+- final Result result = context.processType(typeWithNullability.getType());
+- return new Result(
+- typeWithNullability.isNullable() ? new TsType.NullableType(result.getTsType()) : result.getTsType(),
+- result.getDiscoveredClasses()
+- );
+- }
+ return null;
+ }
+
+- private Result processKnownGenericType(Type javaType, Class> rawClass, Context context) {
+-
+- final Optional> listBaseClass = assignableFrom(known.listClasses, rawClass);
+- if (listBaseClass.isPresent()) {
+- final List resolvedGenericVariables = GenericsResolver.resolveBaseGenericVariables(listBaseClass.get(), javaType);
+- final Result result = context.processTypeInsideCollection(resolvedGenericVariables.get(0));
+- return new Result(new TsType.BasicArrayType(result.getTsType()), result.getDiscoveredClasses());
+- }
+-
+- final Optional> mapBaseClass = assignableFrom(known.mapClasses, rawClass);
+- if (mapBaseClass.isPresent()) {
+- final List resolvedGenericVariables = GenericsResolver.resolveBaseGenericVariables(mapBaseClass.get(), javaType);
+- final Result keyResult = context.processType(resolvedGenericVariables.get(0));
+- final Result valueResult = context.processTypeInsideCollection(resolvedGenericVariables.get(1));
+- final TsType valueTsType = valueResult.getTsType();
+- if (keyResult.getTsType() instanceof TsType.EnumReferenceType) {
+- return new Result(
+- new TsType.MappedType(keyResult.getTsType(), TsType.MappedType.QuestionToken.Question, valueTsType),
+- Utils.concat(keyResult.getDiscoveredClasses(), valueResult.getDiscoveredClasses())
+- );
+- } else {
+- return new Result(
+- new TsType.IndexedArrayType(TsType.String, valueTsType),
+- valueResult.getDiscoveredClasses()
+- );
+- }
+- }
+-
+- final Optional> optionalBaseClass = assignableFrom(known.optionalClasses, rawClass);
+- if (optionalBaseClass.isPresent()) {
+- final List resolvedGenericVariables = GenericsResolver.resolveBaseGenericVariables(optionalBaseClass.get(), javaType);
+- final Result result = context.processType(resolvedGenericVariables.get(0));
+- return new Result(result.getTsType().optional(), result.getDiscoveredClasses());
+- }
+-
+- final Optional> wrapperBaseClass = assignableFrom(known.wrapperClasses, rawClass);
+- if (wrapperBaseClass.isPresent()) {
+- final List resolvedGenericVariables = GenericsResolver.resolveBaseGenericVariables(wrapperBaseClass.get(), javaType);
+- final Result result = context.processType(resolvedGenericVariables.get(0));
+- return new Result(result.getTsType(), result.getDiscoveredClasses());
+- }
+-
+- return null;
++ private static Map getKnownTypes() {
++ final Map knownTypes = new LinkedHashMap<>();
++ // java.lang
++ knownTypes.put(Object.class, TsType.Any);
++ knownTypes.put(Byte.class, TsType.Number);
++ knownTypes.put(Byte.TYPE, TsType.Number);
++ knownTypes.put(Short.class, TsType.Number);
++ knownTypes.put(Short.TYPE, TsType.Number);
++ knownTypes.put(Integer.class, TsType.Number);
++ knownTypes.put(Integer.TYPE, TsType.Number);
++ knownTypes.put(Long.class, TsType.Number);
++ knownTypes.put(Long.TYPE, TsType.Number);
++ knownTypes.put(Float.class, TsType.Number);
++ knownTypes.put(Float.TYPE, TsType.Number);
++ knownTypes.put(Double.class, TsType.Number);
++ knownTypes.put(Double.TYPE, TsType.Number);
++ knownTypes.put(Boolean.class, TsType.Boolean);
++ knownTypes.put(Boolean.TYPE, TsType.Boolean);
++ knownTypes.put(Character.class, TsType.String);
++ knownTypes.put(Character.TYPE, TsType.String);
++ knownTypes.put(String.class, TsType.String);
++ knownTypes.put(void.class, TsType.Void);
++ knownTypes.put(Void.class, TsType.Void);
++ // other java packages
++ knownTypes.put(BigDecimal.class, TsType.Number);
++ knownTypes.put(BigInteger.class, TsType.Number);
++ knownTypes.put(Date.class, TsType.Date);
++ knownTypes.put(UUID.class, TsType.String);
++ return knownTypes;
+ }
+
+- private static LoadedDataLibraries getKnownClasses() {
+- return new LoadedDataLibraries(
+- Arrays.asList(char.class, Character.class, String.class, UUID.class),
+- Arrays.asList(byte.class, short.class, int.class, long.class, float.class, double.class, Number.class),
+- Arrays.asList(boolean.class, Boolean.class),
+- Arrays.asList(Date.class, Calendar.class, Temporal.class),
+- Arrays.asList(),
+- Arrays.asList(void.class, Void.class),
+- Arrays.asList(Collection.class),
+- Arrays.asList(Map.class),
+- Arrays.asList(Optional.class),
+- Arrays.asList(jakarta.xml.bind.JAXBElement.class, javax.xml.bind.JAXBElement.class),
+- Arrays.asList(),
+- Arrays.asList()
+- );
+- }
++ private static final Map KnownTypes = getKnownTypes();
+
+ }
+diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/DeprecationText.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/DeprecationText.java
+deleted file mode 100644
+index d93133f89..000000000
+--- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/DeprecationText.java
++++ /dev/null
+@@ -1,11 +0,0 @@
+-
+-package cz.habarta.typescript.generator;
+-
+-import java.lang.annotation.Retention;
+-import java.lang.annotation.RetentionPolicy;
+-
+-
+-@Retention(RetentionPolicy.RUNTIME)
+-public @interface DeprecationText {
+- String value();
+-}
+diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/EnumMapping.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/EnumMapping.java
+index bbd85aba5..6cc850f12 100644
+--- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/EnumMapping.java
++++ b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/EnumMapping.java
+@@ -3,5 +3,5 @@ package cz.habarta.typescript.generator;
+
+
+ public enum EnumMapping {
+- asUnion, asInlineUnion, asEnum, asNumberBasedEnum
++ asUnion, asInlineUnion, asNumberBasedEnum
+ }
+diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/ExcludingTypeProcessor.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/ExcludingTypeProcessor.java
+index e37601b4e..a7f897439 100644
+--- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/ExcludingTypeProcessor.java
++++ b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/ExcludingTypeProcessor.java
+@@ -1,29 +1,15 @@
+
+ package cz.habarta.typescript.generator;
+
++import cz.habarta.typescript.generator.util.Predicate;
+ import cz.habarta.typescript.generator.util.Utils;
+ import java.lang.reflect.Type;
+-import java.util.Collections;
+-import java.util.LinkedHashSet;
+-import java.util.List;
+-import java.util.Set;
+-import java.util.function.Predicate;
+
+
+ public class ExcludingTypeProcessor implements TypeProcessor {
+
+ private final Predicate excludeFilter;
+
+- public ExcludingTypeProcessor(List excludedTypes) {
+- this(new Predicate() {
+- final Set excludedTypesSet = excludedTypes != null ? new LinkedHashSet<>(excludedTypes) : Collections.emptySet();
+- @Override
+- public boolean test(String typeName) {
+- return excludedTypesSet.contains(typeName);
+- }
+- });
+- }
+-
+ public ExcludingTypeProcessor(Predicate excludeFilter) {
+ this.excludeFilter = excludeFilter;
+ }
+diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Extension.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Extension.java
+deleted file mode 100644
+index 1adc9fdad..000000000
+--- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Extension.java
++++ /dev/null
+@@ -1,47 +0,0 @@
+-
+-package cz.habarta.typescript.generator;
+-
+-import cz.habarta.typescript.generator.compiler.ModelCompiler;
+-import cz.habarta.typescript.generator.compiler.ModelTransformer;
+-import cz.habarta.typescript.generator.compiler.TsModelTransformer;
+-import cz.habarta.typescript.generator.emitter.EmitterExtension;
+-import java.util.Collections;
+-import java.util.List;
+-import java.util.Map;
+-
+-
+-public abstract class Extension extends EmitterExtension {
+-
+- public void setConfiguration(Map configuration) throws RuntimeException {
+- }
+-
+- public List getTransformers() {
+- return Collections.emptyList();
+- }
+-
+- public static class TransformerDefinition {
+- public final ModelCompiler.TransformationPhase phase;
+- public final ModelTransformer transformer;
+- public final TsModelTransformer tsTransformer;
+-
+- public TransformerDefinition(ModelCompiler.TransformationPhase phase, ModelTransformer transformer) {
+- if (phase != ModelCompiler.TransformationPhase.BeforeTsModel) {
+- throw new IllegalArgumentException("ModelTransformer can only be applied in phase 'BeforeTsModel'");
+- }
+- this.phase = phase;
+- this.transformer = transformer;
+- this.tsTransformer = null;
+- }
+-
+- public TransformerDefinition(ModelCompiler.TransformationPhase phase, TsModelTransformer transformer) {
+- if (phase == ModelCompiler.TransformationPhase.BeforeTsModel) {
+- throw new IllegalArgumentException("TsModelTransformer cannot be applied in phase 'BeforeTsModel'");
+- }
+- this.phase = phase;
+- this.transformer = null;
+- this.tsTransformer = transformer;
+- }
+-
+- }
+-
+-}
+diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/GenericsTypeProcessor.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/GenericsTypeProcessor.java
+new file mode 100644
+index 000000000..2d2b39d6f
+--- /dev/null
++++ b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/GenericsTypeProcessor.java
+@@ -0,0 +1,53 @@
++
++package cz.habarta.typescript.generator;
++
++import cz.habarta.typescript.generator.compiler.Symbol;
++import java.lang.reflect.*;
++import java.util.*;
++
++
++public class GenericsTypeProcessor implements TypeProcessor {
++
++ @Override
++ public TypeProcessor.Result processType(Type javaType, TypeProcessor.Context context) {
++ if (javaType instanceof TypeVariable) {
++ final TypeVariable> typeVariable = (TypeVariable) javaType;
++ return new Result(new TsType.GenericVariableType(typeVariable.getName()));
++ }
++ if (javaType instanceof Class) {
++ final Class> javaClass = (Class>) javaType;
++ if (javaClass.getTypeParameters().length > 0) {
++ return processGenericClass(javaClass, javaClass.getTypeParameters(), context);
++ }
++ }
++ if (javaType instanceof ParameterizedType) {
++ final ParameterizedType parameterizedType = (ParameterizedType) javaType;
++ if (parameterizedType.getRawType() instanceof Class) {
++ final Class> javaClass = (Class>) parameterizedType.getRawType();
++ return processGenericClass(javaClass, parameterizedType.getActualTypeArguments(), context);
++ }
++ }
++ return null;
++ }
++
++ private Result processGenericClass(Class> rawType, Type[] typeArguments, TypeProcessor.Context context) {
++ if (!Collection.class.isAssignableFrom(rawType) && !Map.class.isAssignableFrom(rawType)) {
++ final List> discoveredClasses = new ArrayList<>();
++ // raw type
++ final Symbol rawSymbol = context.getSymbol(rawType);
++ discoveredClasses.add(rawType);
++ // type arguments
++ final List tsTypeArguments = new ArrayList<>();
++ for (Type typeArgument : typeArguments) {
++ final TypeProcessor.Result typeArgumentResult = context.processType(typeArgument);
++ tsTypeArguments.add(typeArgumentResult.getTsType());
++ discoveredClasses.addAll(typeArgumentResult.getDiscoveredClasses());
++ }
++ // result
++ final TsType.GenericReferenceType type = new TsType.GenericReferenceType(rawSymbol, tsTypeArguments);
++ return new Result(type, discoveredClasses);
++ }
++ return null;
++ }
++
++}
+diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/GsonConfiguration.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/GsonConfiguration.java
+deleted file mode 100644
+index 6a3f6a915..000000000
+--- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/GsonConfiguration.java
++++ /dev/null
+@@ -1,21 +0,0 @@
+-
+-package cz.habarta.typescript.generator;
+-
+-
+-/**
+- * This class is used for configuration in Maven and Gradle plugins so we need
+- * to pay attention to use only types supported in both build plugins.
+- */
+-public class GsonConfiguration {
+-
+- /**
+- * Excludes all class fields that have the specified modifiers.
+- * Modifiers are separated with | character.
+- * Field exclusion modifiers are public | protected | private | static | final | transient | volatile.
+- * Default value is static | transient (the same as in Gson itself).
+- * Note: single charater | can be used to pass empty list of modifiers
+- * (in Maven empty string is interpreted as null which means "not set").
+- */
+- public String excludeFieldsWithModifiers;
+-
+-}
+diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/IdentifierCasing.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/IdentifierCasing.java
+deleted file mode 100644
+index 4b6e4264c..000000000
+--- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/IdentifierCasing.java
++++ /dev/null
+@@ -1,10 +0,0 @@
+-
+-package cz.habarta.typescript.generator;
+-
+-
+-public enum IdentifierCasing {
+- keepOriginal,
+- PascalCase,
+- camelCase,
+-// UPPER_CASE,
+-}
+diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Input.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Input.java
+index 6a7275988..9e1c21965 100644
+--- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Input.java
++++ b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Input.java
+@@ -1,21 +1,13 @@
+
+ package cz.habarta.typescript.generator;
+
+-import cz.habarta.typescript.generator.parser.SourceType;
+-import cz.habarta.typescript.generator.util.Utils;
+-import io.github.classgraph.ClassGraph;
+-import io.github.classgraph.ScanResult;
+-import java.lang.reflect.Type;
+-import java.net.URLClassLoader;
+-import java.util.ArrayList;
+-import java.util.Arrays;
+-import java.util.Collections;
+-import java.util.Date;
+-import java.util.List;
+-import java.util.Objects;
+-import java.util.function.Predicate;
++import cz.habarta.typescript.generator.parser.*;
++import cz.habarta.typescript.generator.util.Predicate;
++import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner;
++import java.lang.reflect.*;
++import java.util.*;
++import java.util.regex.Matcher;
+ import java.util.regex.Pattern;
+-import java.util.stream.Collectors;
+
+
+ public class Input {
+@@ -31,7 +23,6 @@ public class Input {
+ }
+
+ public static Input from(Type... types) {
+- Objects.requireNonNull(types, "types");
+ final List> sourceTypes = new ArrayList<>();
+ for (Type type : types) {
+ sourceTypes.add(new SourceType<>(type));
+@@ -39,173 +30,117 @@ public class Input {
+ return new Input(sourceTypes);
+ }
+
+- public static class Parameters {
+- public List classNames;
+- public List classNamePatterns;
+- public List classesWithAnnotations;
+- public List classesImplementingInterfaces;
+- public List classesExtendingClasses;
+- public String jaxrsApplicationClassName;
+- public boolean automaticJaxrsApplication;
+- public Predicate isClassNameExcluded;
+- public URLClassLoader classLoader;
+- public List scanningAcceptedPackages;
+- public boolean debug;
+- }
+-
+- public static Input from(Parameters parameters) {
+- final ClassLoader originalContextClassLoader = Thread.currentThread().getContextClassLoader();
++ private static Input fromClassNames(List classNames) {
+ try {
+- if (parameters.classLoader != null) {
+- Thread.currentThread().setContextClassLoader(parameters.classLoader);
+- }
+- try (final ClasspathScanner classpathScanner = new ClasspathScanner(parameters.classLoader, parameters.scanningAcceptedPackages, parameters.debug)) {
+- final List> types = new ArrayList<>();
+- if (parameters.classNames != null) {
+- types.addAll(fromClassNames(parameters.classNames));
+- }
+- if (parameters.classNamePatterns != null) {
+- types.addAll(fromClassNamePatterns(classpathScanner.getScanResult(), parameters.classNamePatterns));
+- }
+- if (parameters.classesImplementingInterfaces != null) {
+- final ScanResult scanResult = classpathScanner.getScanResult();
+- final List> c = fromClassNames(parameters.classesImplementingInterfaces.stream()
+- .flatMap(interf -> scanResult.getClassesImplementing(interf).getNames().stream())
+- .distinct()
+- .collect(Collectors.toList())
+- );
+- types.addAll(c);
+- }
+- if (parameters.classesExtendingClasses != null) {
+- final ScanResult scanResult = classpathScanner.getScanResult();
+- final List> c = fromClassNames(parameters.classesExtendingClasses.stream()
+- .flatMap(superclass -> scanResult.getSubclasses(superclass).getNames().stream())
+- .distinct()
+- .collect(Collectors.toList())
+- );
+- types.addAll(c);
+- }
+- if (parameters.classesWithAnnotations != null) {
+- final ScanResult scanResult = classpathScanner.getScanResult();
+- types.addAll(fromClassNames(parameters.classesWithAnnotations.stream()
+- .flatMap(annotation -> scanResult.getClassesWithAnnotation(annotation).getNames().stream())
+- .distinct()
+- .collect(Collectors.toList())
+- ));
+- }
+- if (parameters.jaxrsApplicationClassName != null) {
+- types.addAll(fromClassNames(Arrays.asList(parameters.jaxrsApplicationClassName)));
+- }
+- if (parameters.automaticJaxrsApplication) {
+- types.addAll(JaxrsApplicationScanner.scanAutomaticJaxrsApplication(classpathScanner.getScanResult(), parameters.isClassNameExcluded));
+- }
+- if (types.isEmpty()) {
+- final String errorMessage = "No input classes found.";
+- TypeScriptGenerator.getLogger().error(errorMessage);
+- throw new RuntimeException(errorMessage);
+- }
+- return new Input(types);
+- }
+- } finally {
+- Thread.currentThread().setContextClassLoader(originalContextClassLoader);
+- }
+- }
+-
+- private static class ClasspathScanner implements AutoCloseable {
+-
+- private final URLClassLoader classLoader;
+- private final List acceptedPackages;
+- private final boolean verbose;
+- private ScanResult scanResult = null;
+-
+- public ClasspathScanner(URLClassLoader classLoader, List acceptedPackages, boolean verbose) {
+- this.classLoader = classLoader;
+- this.acceptedPackages = acceptedPackages;
+- this.verbose = verbose;
+- }
+-
+- public ScanResult getScanResult() {
+- if (scanResult == null) {
+- TypeScriptGenerator.getLogger().info("Scanning classpath");
+- final Date scanStart = new Date();
+- ClassGraph classGraph = new ClassGraph()
+- .enableClassInfo()
+- .enableAnnotationInfo()
+- .ignoreClassVisibility();
+- if (classLoader != null) {
+- classGraph = classGraph.overrideClasspath((Object[])classLoader.getURLs());
+- }
+- if (acceptedPackages != null && !acceptedPackages.isEmpty()) {
+- classGraph = classGraph.acceptPackages(acceptedPackages.toArray(new String[0]));
+- }
+- if (verbose) {
+- classGraph = classGraph.verbose();
++ final List> types = new ArrayList<>();
++ for (String className : classNames) {
++ final Class> cls = Thread.currentThread().getContextClassLoader().loadClass(className);
++ // skip synthetic classes (as those generated by java compiler for switch with enum)
++ // and anonymous classes (should not be processed and they do not have SimpleName)
++ if (!cls.isSynthetic() && !cls.isAnonymousClass()) {
++ types.add(new SourceType(cls, null, null));
+ }
+- final ScanResult result = classGraph.scan();
+- final int count = result.getAllClasses().size();
+- final Date scanEnd = new Date();
+- final double timeInSeconds = (scanEnd.getTime() - scanStart.getTime()) / 1000.0;
+- TypeScriptGenerator.getLogger().info(String.format("Scanning finished in %.2f seconds. Total number of classes: %d.", timeInSeconds, count));
+- scanResult = result;
+- }
+- return scanResult;
+- }
+-
+- @Override
+- public void close() {
+- if (scanResult != null) {
+- scanResult.close();
+ }
++ return new Input(types);
++ } catch (ReflectiveOperationException e) {
++ throw new RuntimeException(e);
+ }
+-
+ }
+
+- private static List> fromClassNamePatterns(ScanResult scanResult, List classNamePatterns) {
++ private static Input fromClassNamePatterns(List classNamePatterns) {
++ System.out.println("Scanning classpath");
++ final FastClasspathScanner scanner = new FastClasspathScanner().scan();
+ final List allClassNames = new ArrayList<>();
+- allClassNames.addAll(scanResult.getAllStandardClasses().getNames());
+- allClassNames.addAll(scanResult.getAllInterfaces().getNames());
++ allClassNames.addAll(scanner.getNamesOfAllStandardClasses());
++ allClassNames.addAll(scanner.getNamesOfAllInterfaceClasses());
+ Collections.sort(allClassNames);
+ final List classNames = filterClassNames(allClassNames, classNamePatterns);
+- TypeScriptGenerator.getLogger().info(String.format("Found %d classes matching pattern.", classNames.size()));
++ System.out.println(String.format("Matched: %d, total: %d.", classNames.size(), allClassNames.size()));
+ return fromClassNames(classNames);
+ }
+
+- private static List> fromClassNames(List classNames) {
+- final List> types = new ArrayList<>();
+- for (Class> cls : loadClasses(classNames)) {
+- // skip synthetic classes (as those generated by java compiler for switch with enum)
+- // and anonymous classes (should not be processed and they do not have SimpleName)
+- if (!cls.isSynthetic() && !cls.isAnonymousClass()) {
+- types.add(new SourceType<>(cls, null, null));
+- }
+- }
+- return types;
++ private static Input fromJaxrsApplication(String jaxrsApplicationClassName, Predicate isClassNameExcluded) {
++ final List> sourceTypes = new JaxrsApplicationScanner().scanJaxrsApplication(jaxrsApplicationClassName, isClassNameExcluded);
++ return new Input(sourceTypes);
+ }
+
+- static List> loadClasses(List classNames) {
+- final List> classes = new ArrayList<>();
+- for (String className : classNames) {
+- try {
+- final Class> cls = Thread.currentThread().getContextClassLoader().loadClass(className);
+- classes.add(cls);
+- } catch (ReflectiveOperationException e) {
+- final String errorMessage = String.format("Cannot load class '%s'", className);
+- TypeScriptGenerator.getLogger().error(errorMessage);
+- throw new RuntimeException(errorMessage, e);
++ public static Input fromClassNamesAndJaxrsApplication(List classNames, List classNamePatterns, String jaxrsApplicationClassName, boolean automaticJaxrsApplication, Predicate isClassNameExcluded, ClassLoader classLoader) {
++ final ClassLoader originalContextClassLoader = Thread.currentThread().getContextClassLoader();
++ try {
++ Thread.currentThread().setContextClassLoader(classLoader);
++ final List> types = new ArrayList<>();
++ if (classNames != null) {
++ types.addAll(fromClassNames(classNames).getSourceTypes());
++ }
++ if (classNamePatterns != null) {
++ types.addAll(fromClassNamePatterns(classNamePatterns).getSourceTypes());
++ }
++ if (jaxrsApplicationClassName != null) {
++ types.addAll(fromJaxrsApplication(jaxrsApplicationClassName, isClassNameExcluded).getSourceTypes());
+ }
++ if (automaticJaxrsApplication) {
++ types.addAll(fromJaxrsApplication(null, isClassNameExcluded).getSourceTypes());
++ }
++ if (types.isEmpty()) {
++ final String errorMessage = "No input classes found.";
++ System.out.println(errorMessage);
++ throw new RuntimeException(errorMessage);
++ }
++ return new Input(types);
++ } finally {
++ Thread.currentThread().setContextClassLoader(originalContextClassLoader);
+ }
+- return classes;
+ }
+
+ static List filterClassNames(List classNames, List globs) {
+- final List regexps = Utils.globsToRegexps(globs);
++ final List regexps = globsToRegexps(globs);
+ final List result = new ArrayList<>();
+ for (String className : classNames) {
+- if (Utils.classNameMatches(className, regexps)) {
++ if (classNameMatches(className, regexps)) {
+ result.add(className);
+ }
+ }
+ return result;
+ }
+
++ static boolean classNameMatches(String className, List regexps) {
++ for (Pattern regexp : regexps) {
++ if (regexp.matcher(className).matches()) {
++ return true;
++ }
++ }
++ return false;
++ }
++
++ static List globsToRegexps(List globs) {
++ final List regexps = new ArrayList<>();
++ for (String glob : globs) {
++ regexps.add(globToRegexp(glob));
++ }
++ return regexps;
++ }
++
++ /**
++ * Creates regexp for glob pattern.
++ * Replaces "*" with "[^.\$]*" and "**" with ".*".
++ */
++ static Pattern globToRegexp(String glob) {
++ final Pattern globToRegexpPattern = Pattern.compile("(\\*\\*)|(\\*)");
++ final Matcher matcher = globToRegexpPattern.matcher(glob);
++ final StringBuffer sb = new StringBuffer();
++ int lastEnd = 0;
++ while (matcher.find()) {
++ sb.append(Pattern.quote(glob.substring(lastEnd, matcher.start())));
++ if (matcher.group(1) != null) {
++ sb.append(Matcher.quoteReplacement(".*"));
++ }
++ if (matcher.group(2) != null) {
++ sb.append(Matcher.quoteReplacement("[^.$]*"));
++ }
++ lastEnd = matcher.end();
++ }
++ sb.append(Pattern.quote(glob.substring(lastEnd, glob.length())));
++ return Pattern.compile(sb.toString());
++ }
++
+ }
+diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Jackson2Configuration.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Jackson2Configuration.java
+deleted file mode 100644
+index d1db7845e..000000000
+--- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Jackson2Configuration.java
++++ /dev/null
+@@ -1,85 +0,0 @@
+-
+-package cz.habarta.typescript.generator;
+-
+-import com.fasterxml.jackson.annotation.JsonAutoDetect;
+-import java.util.List;
+-
+-
+-/**
+- * This class is used for configuration in Maven and Gradle plugins
+- * so we need to pay attention to use only types supported in both build plugins.
+- */
+-public class Jackson2Configuration {
+-
+- /**
+- * Minimum visibility required for fields to be auto-detected.
+- */
+- public JsonAutoDetect.Visibility fieldVisibility;
+-
+- /**
+- * Minimum visibility required for getters to be auto-detected (doesn't include "is getters").
+- */
+- public JsonAutoDetect.Visibility getterVisibility;
+-
+- /**
+- * Minimum visibility required for "is getters" to be auto-detected.
+- */
+- public JsonAutoDetect.Visibility isGetterVisibility;
+-
+- /**
+- * Minimum visibility required for setters to be auto-detected.
+- */
+- public JsonAutoDetect.Visibility setterVisibility;
+-
+- /**
+- * Minimum visibility required for creators to be auto-detected.
+- */
+- public JsonAutoDetect.Visibility creatorVisibility;
+-
+- /**
+- * Shape format overrides for specified classes.
+- * Multiple overrides can be specified, each using this format: javaClassName:shape
+- * where shape is one of the values from
+- * JsonFormat.Shape enum.
+- * Example: java.util.Map$Entry:OBJECT
+- */
+- public List shapeConfigOverrides;
+-
+- /**
+- * Feature that determines standard Enum values representation:
+- * if enabled, return value of Enum.toString() is used;
+- * if disabled, return value of Enum.name() is used.
+- * (In ObjectMapper this feature is controlled using
+- * SerializationFeature.WRITE_ENUMS_USING_TO_STRING and
+- * DeserializationFeature.READ_ENUMS_USING_TO_STRING constants.)
+- * Default value is false.
+- */
+- public boolean enumsUsingToString;
+-
+- /**
+- * Disables processing of @JsonIdentityInfo and @JsonIdentityReference annotations.
+- * Can be useful for example when using JSOG library which uses IDs to serialize and deserialize object graphs.
+- */
+- public boolean disableObjectIdentityFeature;
+-
+- /**
+- * Types produced by JsonSerializers.
+- * Multiple mappings can be specified, each using following format: serializerClassName:typescriptType.
+- * Example: org.example.IdSerializer:string or org.example.IdSerializer:{ id: string }
+- */
+- public List serializerTypeMappings;
+-
+- /**
+- * Types produced by JsonDeserializers.
+- * Multiple mappings can be specified, each using following format: deserializerClassName:typescriptType.
+- * Example: org.example.MyDeserializer:string
+- */
+- public List deserializerTypeMappings;
+-
+- /**
+- * Specifies ObjectMapper's active view (as a fully-qualified class name).
+- * Properties can be annotated with @JsonView to indicate which views they are part of.
+- */
+- public String view;
+-
+-}
+diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Jackson2ConfigurationResolved.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Jackson2ConfigurationResolved.java
+deleted file mode 100644
+index 0fbd5d094..000000000
+--- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Jackson2ConfigurationResolved.java
++++ /dev/null
+@@ -1,78 +0,0 @@
+-
+-package cz.habarta.typescript.generator;
+-
+-import com.fasterxml.jackson.annotation.JsonAutoDetect;
+-import com.fasterxml.jackson.annotation.JsonFormat;
+-import com.fasterxml.jackson.databind.JsonDeserializer;
+-import com.fasterxml.jackson.databind.JsonSerializer;
+-import java.util.LinkedHashMap;
+-import java.util.List;
+-import java.util.Map;
+-import java.util.function.Function;
+-
+-
+-public class Jackson2ConfigurationResolved {
+-
+- public JsonAutoDetect.Visibility fieldVisibility;
+- public JsonAutoDetect.Visibility getterVisibility;
+- public JsonAutoDetect.Visibility isGetterVisibility;
+- public JsonAutoDetect.Visibility setterVisibility;
+- public JsonAutoDetect.Visibility creatorVisibility;
+- public Map, JsonFormat.Shape> shapeConfigOverrides;
+- public boolean enumsUsingToString;
+- public boolean disableObjectIdentityFeature;
+- @SuppressWarnings("rawtypes")
+- public Map, String> serializerTypeMappings;
+- @SuppressWarnings("rawtypes")
+- public Map, String> deserializerTypeMappings;
+- public Class> view;
+-
+- public static Jackson2ConfigurationResolved from(Jackson2Configuration configuration, ClassLoader classLoader) {
+- final Jackson2ConfigurationResolved resolved = new Jackson2ConfigurationResolved();
+- resolved.fieldVisibility = configuration.fieldVisibility;
+- resolved.getterVisibility = configuration.getterVisibility;
+- resolved.isGetterVisibility = configuration.isGetterVisibility;
+- resolved.setterVisibility = configuration.setterVisibility;
+- resolved.creatorVisibility = configuration.creatorVisibility;
+- resolved.fieldVisibility = configuration.fieldVisibility;
+- resolved.shapeConfigOverrides = resolveClassMappings(
+- configuration.shapeConfigOverrides, "shapeConfigOverride", classLoader, Object.class, JsonFormat.Shape::valueOf);
+- resolved.enumsUsingToString = configuration.enumsUsingToString;
+- resolved.disableObjectIdentityFeature = configuration.disableObjectIdentityFeature;
+- resolved.deserializerTypeMappings = resolveClassMappings(
+- configuration.deserializerTypeMappings, "deserializerTypeMapping", classLoader, JsonDeserializer.class, Function.identity());
+- resolved.serializerTypeMappings = resolveClassMappings(
+- configuration.serializerTypeMappings, "serializerTypeMapping", classLoader, JsonSerializer.class, Function.identity());
+- resolved.view = configuration.view != null ? Settings.loadClass(classLoader, configuration.view, Object.class) : null;
+- return resolved;
+- }
+-
+- private static Map, V> resolveClassMappings(List mappings, String mappingName, ClassLoader classLoader,
+- Class extends C> key, Function valueConvertor) {
+- if (mappings == null) {
+- return null;
+- }
+- final Map, V> resolvedMappings = new LinkedHashMap<>();
+- final Map mappingsMap = Settings.convertToMap(mappings, mappingName);
+- for (Map.Entry entry : mappingsMap.entrySet()) {
+- final Class extends C> cls = Settings.loadClass(classLoader, entry.getKey(), key);
+- final V value = valueConvertor.apply(entry.getValue());
+- resolvedMappings.put(cls, value);
+- }
+- return resolvedMappings;
+- }
+-
+- public void setVisibility(
+- JsonAutoDetect.Visibility fieldVisibility,
+- JsonAutoDetect.Visibility getterVisibility,
+- JsonAutoDetect.Visibility isGetterVisibility,
+- JsonAutoDetect.Visibility setterVisibility,
+- JsonAutoDetect.Visibility creatorVisibility) {
+- this.fieldVisibility = fieldVisibility;
+- this.getterVisibility = getterVisibility;
+- this.isGetterVisibility = isGetterVisibility;
+- this.setterVisibility = setterVisibility;
+- this.creatorVisibility = creatorVisibility;
+- }
+-
+-}
+diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/JaxrsApplicationScanner.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/JaxrsApplicationScanner.java
+index 5bbb4cb67..523fbae7b 100644
+--- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/JaxrsApplicationScanner.java
++++ b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/JaxrsApplicationScanner.java
+@@ -1,79 +1,205 @@
+
+ package cz.habarta.typescript.generator;
+
+-import cz.habarta.typescript.generator.parser.SourceType;
++import cz.habarta.typescript.generator.parser.*;
++import cz.habarta.typescript.generator.util.Predicate;
+ import cz.habarta.typescript.generator.util.Utils;
+-import io.github.classgraph.ScanResult;
+-import java.lang.reflect.Constructor;
+-import java.lang.reflect.Type;
+-import java.util.ArrayList;
+-import java.util.Collections;
+-import java.util.List;
+-import java.util.Set;
+-import java.util.function.Predicate;
++import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner;
++import java.lang.annotation.*;
++import java.lang.reflect.*;
++import java.util.*;
++import javax.ws.rs.*;
++import javax.ws.rs.core.*;
+
+
+ public class JaxrsApplicationScanner {
+
+- public static List> scanJaxrsApplication(Class> jaxrsApplicationClass, Predicate isClassNameExcluded) {
+- final ClassLoader originalContextClassLoader = Thread.currentThread().getContextClassLoader();
++ private Predicate isClassNameExcluded;
++ private Set defaultExcludes;
++ private Queue> resourceQueue;
++ private List> discoveredTypes;
++
++ public List> scanJaxrsApplication(String jaxrsApplicationClassName, Predicate isClassNameExcluded) {
+ try {
+- Thread.currentThread().setContextClassLoader(jaxrsApplicationClass.getClassLoader());
+- TypeScriptGenerator.getLogger().info("Scanning JAX-RS application: " + jaxrsApplicationClass.getName());
+- final Constructor> constructor = jaxrsApplicationClass.getDeclaredConstructor();
+- constructor.setAccessible(true);
+- final Object instance = constructor.newInstance();
+- final Set> applicationClasses;
+- if (instance instanceof jakarta.ws.rs.core.Application) {
+- applicationClasses = ((jakarta.ws.rs.core.Application) instance).getClasses();
+- } else if (instance instanceof javax.ws.rs.core.Application) {
+- applicationClasses = ((javax.ws.rs.core.Application) instance).getClasses();
+- } else {
+- applicationClasses = Collections.emptySet();
++ final List> resourceClasses = jaxrsApplicationClassName != null
++ ? scanJaxrsApplicationForJaxrsResources(jaxrsApplicationClassName)
++ : scanClasspathForJaxrsResources();
++ return scanJaxrsApplication(resourceClasses, isClassNameExcluded);
++ } catch (ReflectiveOperationException e) {
++ final String url = "https://github.com/vojtechhabarta/typescript-generator/wiki/JAX-RS-Application";
++ final String message = "Cannot load JAX-RS application. For more information see " + url + ".";
++ System.out.println(message);
++ throw new RuntimeException(message, e);
++ }
++ }
++
++ private static List> scanJaxrsApplicationForJaxrsResources(String jaxrsApplicationClassName) throws ReflectiveOperationException {
++ System.out.println("Scanning JAX-RS application: " + jaxrsApplicationClassName);
++ final Class> jaxrsApplicationClass = Thread.currentThread().getContextClassLoader().loadClass(jaxrsApplicationClassName);
++ final Constructor> constructor = jaxrsApplicationClass.getDeclaredConstructor();
++ constructor.setAccessible(true);
++ final Application application = (Application) constructor.newInstance();
++ return new ArrayList<>(application.getClasses());
++ }
++
++ private static List> scanClasspathForJaxrsResources() throws ReflectiveOperationException {
++ final FastClasspathScanner scanner = new FastClasspathScanner().scan();
++ final List namesOfResourceClasses = scanner.getNamesOfClassesWithAnnotation(Path.class);
++ final List> classes = new ArrayList<>();
++ for (String className : namesOfResourceClasses) {
++ classes.add(Thread.currentThread().getContextClassLoader().loadClass(className));
++ }
++ System.out.println(String.format("Found: %d root resources.", classes.size()));
++ return classes;
++ }
++
++ List> scanJaxrsApplication(List> resourceClasses, Predicate isClassNameExcluded) {
++ resourceQueue = new LinkedList<>();
++ discoveredTypes = new ArrayList<>();
++ this.isClassNameExcluded = isClassNameExcluded;
++ this.defaultExcludes = new LinkedHashSet<>(getDefaultExcludedClassNames());
++ final LinkedHashSet> scannedResources = new LinkedHashSet<>();
++ Collections.sort(resourceClasses, new Comparator>() {
++ @Override
++ public int compare(Class> o1, Class> o2) {
++ return o1.getName().compareToIgnoreCase(o2.getName());
+ }
+- final List> resourceClasses = new ArrayList<>();
+- for (Class> cls : applicationClasses) {
+- if (cls.isAnnotationPresent(jakarta.ws.rs.Path.class) || cls.isAnnotationPresent(javax.ws.rs.Path.class)) {
+- resourceClasses.add(cls);
+- }
++ });
++ for (Class> resourceClass : resourceClasses) {
++ if (resourceClass.isAnnotationPresent(Path.class)) {
++ resourceQueue.add(resourceClass);
++ }
++ }
++ Class> resourceClass;
++ while ((resourceClass = resourceQueue.poll()) != null) {
++ if (!scannedResources.contains(resourceClass) && !isExcluded(resourceClass)) {
++ System.out.println("Scanning JAX-RS resource: " + resourceClass.getName());
++ scanResource(resourceClass);
++ scannedResources.add(resourceClass);
+ }
+- return new JaxrsApplicationScanner().scanJaxrsApplication(jaxrsApplicationClass, resourceClasses, isClassNameExcluded);
+- } catch (ReflectiveOperationException e) {
+- throw reportError(e);
+- } finally {
+- Thread.currentThread().setContextClassLoader(originalContextClassLoader);
+ }
++ return discoveredTypes;
+ }
+
+- public static List> scanAutomaticJaxrsApplication(ScanResult scanResult, Predicate isClassNameExcluded) {
+- final List namesOfResourceClasses = Utils.concat(
+- scanResult.getClassesWithAnnotation(jakarta.ws.rs.Path.class.getName()).getNames(),
+- scanResult.getClassesWithAnnotation(javax.ws.rs.Path.class.getName()).getNames()
+- );
+- final List> resourceClasses = Input.loadClasses(namesOfResourceClasses);
+- TypeScriptGenerator.getLogger().info(String.format("Found %d root resources.", resourceClasses.size()));
+- return new JaxrsApplicationScanner().scanJaxrsApplication(null, resourceClasses, isClassNameExcluded);
++ private void scanResource(Class> resourceClass) {
++ final List methods = Arrays.asList(resourceClass.getMethods());
++ Collections.sort(methods, new Comparator() {
++ @Override
++ public int compare(Method o1, Method o2) {
++ return o1.getName().compareToIgnoreCase(o2.getName());
++ }
++ });
++ for (Method method : methods) {
++ scanResourceMethod(resourceClass, method);
++ }
+ }
+
+- private static RuntimeException reportError(ReflectiveOperationException e) {
+- final String url = "https://github.com/vojtechhabarta/typescript-generator/wiki/JAX-RS-Application";
+- final String message = "Cannot load JAX-RS application. For more information see " + url + ".";
+- TypeScriptGenerator.getLogger().error(message);
+- return new RuntimeException(message, e);
++ private void scanResourceMethod(Class> resourceClass, Method method) {
++ if (isHttpMethod(method)) {
++ // JAX-RS specification - 3.3.2.1 Entity Parameters
++ final Type entityParameterType = getEntityParameterType(method);
++ if (entityParameterType != null) {
++ foundType(entityParameterType, resourceClass, method.getName());
++ }
++ // JAX-RS specification - 3.3.3 Return Type
++ final Class> returnType = method.getReturnType();
++ final Type genericReturnType = method.getGenericReturnType();
++ if (returnType == void.class || returnType == Response.class) {
++ // no discovered Type
++ } else if (genericReturnType instanceof ParameterizedType && returnType == GenericEntity.class) {
++ final ParameterizedType parameterizedReturnType = (ParameterizedType) genericReturnType;
++ foundType(parameterizedReturnType.getActualTypeArguments()[0], resourceClass, method.getName());
++ } else {
++ foundType(genericReturnType, resourceClass, method.getName());
++ }
++ }
++ // JAX-RS specification - 3.4.1 Sub Resources
++ if (method.isAnnotationPresent(Path.class) && !isHttpMethod(method)) {
++ resourceQueue.add(method.getReturnType());
++ }
+ }
+
+- List> scanJaxrsApplication(Class> applicationClass, List> resourceClasses, Predicate isClassNameExcluded) {
+- Collections.sort(resourceClasses, (o1, o2) -> o1.getName().compareToIgnoreCase(o2.getName()));
+- final List> sourceTypes = new ArrayList<>();
+- if (applicationClass != null) {
+- sourceTypes.add(new SourceType<>(applicationClass));
++ private void foundType(Type type, Class> usedInClass, String usedInMember) {
++ if (!isExcluded(type)) {
++ discoveredTypes.add(new SourceType<>(type, usedInClass, usedInMember));
+ }
+- for (Class> resourceClass : resourceClasses) {
+- if (isClassNameExcluded == null || !isClassNameExcluded.test(resourceClass.getName())) {
+- sourceTypes.add(new SourceType<>(resourceClass));
++ }
++
++ private boolean isExcluded(Type type) {
++ final Class> cls = Utils.getRawClassOrNull(type);
++ if (cls != null && isClassNameExcluded != null && isClassNameExcluded.test(cls.getName())) {
++ return true;
++ }
++ if (cls != null && defaultExcludes.contains(cls.getName())) {
++ return true;
++ }
++ for (Class> standardEntityClass : getStandardEntityClasses()) {
++ if (standardEntityClass.isAssignableFrom(cls)) {
++ return true;
++ }
++ }
++ return false;
++ }
++
++ private static boolean isHttpMethod(Method method) {
++ for (Annotation annotation : method.getAnnotations()) {
++ if (annotation.annotationType().isAnnotationPresent(HttpMethod.class)) {
++ return true;
+ }
+ }
+- return sourceTypes;
++ return false;
++ }
++
++ private static Type getEntityParameterType(Method method) {
++ final Annotation[][] parameterAnnotations = method.getParameterAnnotations();
++ for (int i = 0; i < parameterAnnotations.length; i++) {
++ if (!hasAnyAnnotation(parameterAnnotations[i], Arrays.asList(
++ MatrixParam.class,
++ QueryParam.class,
++ PathParam.class,
++ CookieParam.class,
++ HeaderParam.class,
++ Context.class,
++ FormParam.class
++ ))) {
++ return method.getGenericParameterTypes()[i];
++ }
++ }
++ return null;
++ }
++
++ private static boolean hasAnyAnnotation(Annotation[] parameterAnnotations, List> annotationClasses) {
++ for (Class extends Annotation> annotationClass : annotationClasses) {
++ for (Annotation parameterAnnotation : parameterAnnotations) {
++ if (annotationClass.isInstance(parameterAnnotation)) {
++ return true;
++ }
++ }
++ }
++ return false;
++ }
++
++ private static List> getStandardEntityClasses() {
++ // JAX-RS specification - 4.2.4 Standard Entity Providers
++ return Arrays.asList(
++ byte[].class,
++ java.lang.String.class,
++ java.io.InputStream.class,
++ java.io.Reader.class,
++ java.io.File.class,
++ javax.activation.DataSource.class,
++ javax.xml.transform.Source.class,
++ javax.xml.bind.JAXBElement.class,
++ MultivaluedMap.class,
++ StreamingOutput.class,
++ java.lang.Boolean.class, java.lang.Character.class, java.lang.Number.class,
++ long.class, int.class, short.class, byte.class, double.class, float.class, boolean.class, char.class);
++ }
++
++ private static List getDefaultExcludedClassNames() {
++ return Arrays.asList(
++ "org.glassfish.jersey.media.multipart.FormDataBodyPart"
++ );
+ }
+
+ }
+diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/JsonLibrary.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/JsonLibrary.java
+index 07397ee28..04a5f17a2 100644
+--- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/JsonLibrary.java
++++ b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/JsonLibrary.java
+@@ -1,5 +1,7 @@
++
+ package cz.habarta.typescript.generator;
+
++
+ public enum JsonLibrary {
+- jackson2, jaxb, gson, jsonb
++ jackson1, jackson2, jaxb
+ }
+diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/JsonbConfiguration.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/JsonbConfiguration.java
+deleted file mode 100644
+index f407066f2..000000000
+--- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/JsonbConfiguration.java
++++ /dev/null
+@@ -1,14 +0,0 @@
+-
+-package cz.habarta.typescript.generator;
+-
+-/**
+- * This class is used for configuration in Maven and Gradle plugins
+- * so we need to pay attention to use only types supported in both build plugins.
+- */
+-public class JsonbConfiguration {
+-
+- /**
+- * {@link javax.json.bind.config.PropertyNamingStrategy} name.
+- */
+- public String namingStrategy = "IDENTITY";
+-}
+diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/LoadedDataLibraries.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/LoadedDataLibraries.java
+deleted file mode 100644
+index c9f407cb3..000000000
+--- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/LoadedDataLibraries.java
++++ /dev/null
+@@ -1,104 +0,0 @@
+-
+-package cz.habarta.typescript.generator;
+-
+-import java.util.Arrays;
+-import java.util.Collection;
+-import java.util.Collections;
+-import java.util.List;
+-import java.util.Objects;
+-import java.util.function.Function;
+-import java.util.stream.Collectors;
+-
+-
+-public class LoadedDataLibraries {
+-
+- public final List> stringClasses;
+- public final List> numberClasses;
+- public final List> booleanClasses;
+- public final List> dateClasses;
+- public final List> anyClasses;
+- public final List> voidClasses;
+- public final List> listClasses;
+- public final List> mapClasses;
+- public final List> optionalClasses;
+- public final List> wrapperClasses;
+- public final List typeMappings;
+- public final List typeAliases;
+-
+- public LoadedDataLibraries() {
+- this(empty(), empty(), empty(), empty(), empty(), empty(), empty(), empty(), empty(), empty(), empty(), empty());
+- }
+-
+- private static List empty() {
+- return Collections.emptyList();
+- }
+-
+- public LoadedDataLibraries(
+- List> stringClasses,
+- List> numberClasses,
+- List> booleanClasses,
+- List> dateClasses,
+- List> anyClasses,
+- List> voidClasses,
+- List> listClasses,
+- List> mapClasses,
+- List> optionalClasses,
+- List> wrapperClasses,
+- List typeMappings,
+- List typeAliases
+- ) {
+- this.stringClasses = stringClasses;
+- this.numberClasses = numberClasses;
+- this.booleanClasses = booleanClasses;
+- this.dateClasses = dateClasses;
+- this.anyClasses = anyClasses;
+- this.voidClasses = voidClasses;
+- this.listClasses = validateNumberOfGenericParameters(listClasses, 1);
+- this.mapClasses = validateNumberOfGenericParameters(mapClasses, 2);
+- this.optionalClasses = validateNumberOfGenericParameters(optionalClasses, 1);
+- this.wrapperClasses = validateNumberOfGenericParameters(wrapperClasses, 1);
+- this.typeMappings = typeMappings;
+- this.typeAliases = typeAliases;
+- }
+-
+- private static List> validateNumberOfGenericParameters(List> classes, int required) {
+- for (Class> cls : classes) {
+- if (cls.getTypeParameters().length != required) {
+- throw new RuntimeException(String.format(
+- "Data library class '%s' is required to have %d generic type parameters but it has %d",
+- cls.getName(), required, cls.getTypeParameters().length));
+- }
+- }
+- return classes;
+- }
+-
+- public static LoadedDataLibraries join(LoadedDataLibraries... jsons) {
+- return join(Arrays.asList(jsons));
+- }
+-
+- public static LoadedDataLibraries join(List jsons) {
+- return new LoadedDataLibraries(
+- joinMappedLists(jsons, j -> j.stringClasses),
+- joinMappedLists(jsons, j -> j.numberClasses),
+- joinMappedLists(jsons, j -> j.booleanClasses),
+- joinMappedLists(jsons, j -> j.dateClasses),
+- joinMappedLists(jsons, j -> j.anyClasses),
+- joinMappedLists(jsons, j -> j.voidClasses),
+- joinMappedLists(jsons, j -> j.listClasses),
+- joinMappedLists(jsons, j -> j.mapClasses),
+- joinMappedLists(jsons, j -> j.optionalClasses),
+- joinMappedLists(jsons, j -> j.wrapperClasses),
+- joinMappedLists(jsons, j -> j.typeMappings),
+- joinMappedLists(jsons, j -> j.typeAliases)
+- );
+- }
+-
+- private static List joinMappedLists(List list, Function> mapper) {
+- return list.stream()
+- .filter(Objects::nonNull)
+- .map(mapper)
+- .flatMap(Collection::stream)
+- .collect(Collectors.toList());
+- }
+-
+-}
+diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/LoadedModuleDependencies.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/LoadedModuleDependencies.java
+deleted file mode 100644
+index fa93a5a62..000000000
+--- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/LoadedModuleDependencies.java
++++ /dev/null
+@@ -1,106 +0,0 @@
+-
+-package cz.habarta.typescript.generator;
+-
+-import com.fasterxml.jackson.databind.ObjectMapper;
+-import cz.habarta.typescript.generator.emitter.InfoJson;
+-import cz.habarta.typescript.generator.util.Pair;
+-import cz.habarta.typescript.generator.util.Utils;
+-import java.io.IOException;
+-import java.util.LinkedHashMap;
+-import java.util.List;
+-import java.util.Map;
+-import java.util.Objects;
+-import java.util.function.Function;
+-
+-
+-public class LoadedModuleDependencies {
+-
+- private final Map> classMappings = new LinkedHashMap<>();
+-
+- public LoadedModuleDependencies(Settings settings, List dependencies) {
+- if (dependencies == null) {
+- return;
+- }
+- final ObjectMapper objectMapper = Utils.getObjectMapper();
+- final Map importFromMap = new LinkedHashMap<>();
+- final Map importAsMap = new LinkedHashMap<>();
+- final Map globalTypeNames = new LinkedHashMap<>();
+- for (ModuleDependency dependency : dependencies) {
+- try {
+- final Function reportNullParameter = parameterName ->
+- String.format("Missing required configuration parameter '%s' in module dependency: %s", parameterName, dependency);
+- if (dependency.global) {
+- if (dependency.importFrom != null) {
+- throw new RuntimeException(String.format(
+- "'importFrom' parameter is only applicable when 'global' is not set to 'true' (at module dependency %s).", dependency));
+- }
+- if (dependency.importAs != null) {
+- throw new RuntimeException(String.format(
+- "'importAs' parameter is only applicable when 'global' is not set to 'true' (at module dependency %s).", dependency));
+- }
+- } else {
+- Objects.requireNonNull(dependency.importFrom, () -> reportNullParameter.apply("importFrom"));
+- Objects.requireNonNull(dependency.importAs, () -> reportNullParameter.apply("importAs"));
+- }
+- Objects.requireNonNull(dependency.infoJson, () -> reportNullParameter.apply("infoJson"));
+- if (settings.generateNpmPackageJson) {
+- Objects.requireNonNull(dependency.npmPackageName, () -> reportNullParameter.apply("npmPackageName"));
+- Objects.requireNonNull(dependency.npmVersionRange, () -> reportNullParameter.apply("npmVersionRange"));
+- } else {
+- if (dependency.npmPackageName != null) {
+- throw new RuntimeException(String.format(
+- "'npmPackageName' parameter is only applicable when 'generateNpmPackageJson' is set to 'true' (at module dependency %s).", dependency));
+- }
+- if (dependency.npmVersionRange != null) {
+- throw new RuntimeException(String.format(
+- "'npmVersionRange' parameter is only applicable when 'generateNpmPackageJson' is set to 'true' (at module dependency %s).", dependency));
+- }
+- }
+-
+- TypeScriptGenerator.getLogger().info(String.format(
+- "Loading %s module info from: %s", dependency.toShortString(), dependency.infoJson));
+-
+- if (!dependency.global) {
+- final ModuleDependency importFromConflict = importFromMap.put(dependency.importFrom, dependency);
+- if (importFromConflict != null) {
+- throw new RuntimeException(String.format("Duplicate module '%s'", dependency.importFrom));
+- }
+-
+- final ModuleDependency importAsConflict = importAsMap.put(dependency.importAs, dependency);
+- if (importAsConflict != null) {
+- throw new RuntimeException(String.format("Import identifier '%s' already used for module '%s'", dependency.importAs, importAsConflict.importFrom));
+- }
+- }
+-
+- final InfoJson infoJson = objectMapper.readValue(dependency.infoJson, InfoJson.class);
+- for (InfoJson.ClassInfo classInfo : infoJson.classes) {
+- final Pair presentMapping = classMappings.get(classInfo.javaClass);
+- if (presentMapping != null) {
+- TypeScriptGenerator.getLogger().warning(String.format(
+- "Java class '%s' already present in '%s'", classInfo.javaClass, presentMapping.getValue1().infoJson));
+- } else {
+- classMappings.put(classInfo.javaClass, Pair.of(dependency, classInfo.typeName));
+- }
+- final ModuleDependency presentTypeName = globalTypeNames.get(classInfo.typeName);
+- if (presentTypeName != null) {
+- throw new RuntimeException(String.format(
+- "Duplicate TypeScript global name '%s', declared in '%s' and also '%s'", classInfo.typeName, presentTypeName.infoJson, dependency.infoJson));
+- } else {
+- globalTypeNames.put(classInfo.typeName, dependency);
+- }
+- }
+- } catch (IOException e) {
+- throw new RuntimeException(e);
+- }
+- }
+- }
+-
+- public Pair getFullName(Class> cls) {
+- final Pair mapping = classMappings.get(cls.getName());
+- if (mapping != null) {
+- return Pair.of(mapping.getValue1().importAs, mapping.getValue2());
+- }
+- return null;
+- }
+-
+-}
+diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Logger.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Logger.java
+deleted file mode 100644
+index 840f8fcef..000000000
+--- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Logger.java
++++ /dev/null
+@@ -1,43 +0,0 @@
+-
+-package cz.habarta.typescript.generator;
+-
+-
+-public class Logger {
+-
+- private final Level level;
+-
+- public enum Level {
+- Debug, Verbose, Info, Warning, Error;
+- }
+-
+- public Logger() {
+- this(null);
+- }
+-
+- public Logger(Level level) {
+- this.level = level != null ? level : Level.Verbose;
+- }
+-
+- protected void write(Level level, String message) {
+- if (level.compareTo(this.level) >= 0) {
+- System.out.println(message);
+- }
+- }
+-
+- public final void verbose(String message) {
+- write(Level.Verbose, message);
+- }
+-
+- public final void info(String message) {
+- write(Level.Info, message);
+- }
+-
+- public final void warning(String message) {
+- write(Level.Warning, "Warning: " + message);
+- }
+-
+- public final void error(String message) {
+- write(Level.Error, "Error: " + message);
+- }
+-
+-}
+diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/MapMapping.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/MapMapping.java
+deleted file mode 100644
+index 45cfae8b5..000000000
+--- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/MapMapping.java
++++ /dev/null
+@@ -1,7 +0,0 @@
+-
+-package cz.habarta.typescript.generator;
+-
+-
+-public enum MapMapping {
+- asIndexedArray, asRecord
+-}
+diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/ModuleDependency.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/ModuleDependency.java
+deleted file mode 100644
+index 03996074a..000000000
+--- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/ModuleDependency.java
++++ /dev/null
+@@ -1,50 +0,0 @@
+-
+-package cz.habarta.typescript.generator;
+-
+-import com.fasterxml.jackson.annotation.JsonInclude;
+-import cz.habarta.typescript.generator.util.Utils;
+-import java.io.File;
+-
+-
+-@JsonInclude(JsonInclude.Include.NON_DEFAULT)
+-public class ModuleDependency {
+-
+- public boolean global;
+- public String importFrom;
+- public String importAs;
+- public File infoJson;
+- public String npmPackageName;
+- public String npmVersionRange;
+- public boolean peerDependency;
+-
+- public ModuleDependency() {
+- }
+-
+- private ModuleDependency(boolean global, String importFrom, String importAs, File infoJson, String npmPackageName, String npmVersionRange, boolean peerDependency) {
+- this.global = global;
+- this.importFrom = importFrom;
+- this.importAs = importAs;
+- this.infoJson = infoJson;
+- this.npmPackageName = npmPackageName;
+- this.npmVersionRange = npmVersionRange;
+- this.peerDependency = peerDependency;
+- }
+-
+- public static ModuleDependency module(String importFrom, String importAs, File infoJson, String npmPackageName, String npmVersionRange) {
+- return new ModuleDependency(false, importFrom, importAs, infoJson, npmPackageName, npmVersionRange, false);
+- }
+-
+- public static ModuleDependency global(File infoJson) {
+- return new ModuleDependency(true, null, null, infoJson, null, null, false);
+- }
+-
+- @Override
+- public String toString() {
+- return Utils.objectToString(this);
+- }
+-
+- public String toShortString() {
+- return global ? "global" : "'" + importFrom + "'";
+- }
+-
+-}
+diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/NullabilityDefinition.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/NullabilityDefinition.java
+deleted file mode 100644
+index 07f35277a..000000000
+--- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/NullabilityDefinition.java
++++ /dev/null
+@@ -1,41 +0,0 @@
+-
+-package cz.habarta.typescript.generator;
+-
+-import java.util.Arrays;
+-import java.util.List;
+-
+-
+-public enum NullabilityDefinition {
+-
+- nullAndUndefinedUnion (false, TsType.Null, TsType.Undefined),
+- nullUnion (false, TsType.Null),
+- undefinedUnion (false, TsType.Undefined),
+- nullAndUndefinedInlineUnion (true, TsType.Null, TsType.Undefined),
+- nullInlineUnion (true, TsType.Null),
+- undefinedInlineUnion (true, TsType.Undefined);
+-
+- private final boolean isInline;
+- private final List types;
+-
+- private NullabilityDefinition(boolean isInline, TsType... types) {
+- this.isInline = isInline;
+- this.types = Arrays.asList(types);
+- }
+-
+- public boolean isInline() {
+- return isInline;
+- }
+-
+- public List getTypes() {
+- return types;
+- }
+-
+- public boolean containsUndefined() {
+- return types.contains(TsType.Undefined);
+- }
+-
+- public boolean containsNull() {
+- return types.contains(TsType.Null);
+- }
+-
+-}
+diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/OptionalProperties.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/OptionalProperties.java
+deleted file mode 100644
+index d2bfb8f26..000000000
+--- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/OptionalProperties.java
++++ /dev/null
+@@ -1,7 +0,0 @@
+-
+-package cz.habarta.typescript.generator;
+-
+-
+-public enum OptionalProperties {
+- useSpecifiedAnnotations, useLibraryDefinition, all
+-}
+diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/OptionalPropertiesDeclaration.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/OptionalPropertiesDeclaration.java
+deleted file mode 100644
+index 8729260b4..000000000
+--- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/OptionalPropertiesDeclaration.java
++++ /dev/null
+@@ -1,11 +0,0 @@
+-
+-package cz.habarta.typescript.generator;
+-
+-
+-public enum OptionalPropertiesDeclaration {
+- questionMark,
+- questionMarkAndNullableType,
+- nullableType,
+- nullableAndUndefinableType,
+- undefinableType,
+-}
+diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Output.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Output.java
+index c6d4d8181..c8dc64c9d 100644
+--- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Output.java
++++ b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Output.java
+@@ -1,13 +1,8 @@
+
+ package cz.habarta.typescript.generator;
+
+-import java.io.File;
+-import java.io.FileNotFoundException;
+-import java.io.FileOutputStream;
+-import java.io.OutputStream;
+-import java.io.OutputStreamWriter;
+-import java.io.Writer;
+-import java.nio.charset.Charset;
++import java.io.*;
++import java.nio.charset.*;
+
+
+ public class Output {
+diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/RestNamespacing.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/RestNamespacing.java
+deleted file mode 100644
+index a2b9c26a8..000000000
+--- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/RestNamespacing.java
++++ /dev/null
+@@ -1,9 +0,0 @@
+-
+-package cz.habarta.typescript.generator;
+-
+-
+-public enum RestNamespacing {
+-
+- singleObject, perResource, byAnnotation
+-
+-}
+diff --git a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Settings.java b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Settings.java
+index 729aa8be8..0a169945c 100644
+--- a/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Settings.java
++++ b/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Settings.java
+@@ -1,47 +1,16 @@
+
+ package cz.habarta.typescript.generator;
+
+-import com.fasterxml.jackson.core.JsonParser;
+-import com.fasterxml.jackson.databind.Module;
+-import com.fasterxml.jackson.databind.ObjectMapper;
+-import cz.habarta.typescript.generator.compiler.ModelCompiler;
+-import cz.habarta.typescript.generator.compiler.SymbolTable.CustomTypeNamingFunction;
+ import cz.habarta.typescript.generator.emitter.EmitterExtension;
+-import cz.habarta.typescript.generator.emitter.EmitterExtensionFeatures;
+-import cz.habarta.typescript.generator.parser.JaxrsApplicationParser;
+-import cz.habarta.typescript.generator.parser.RestApplicationParser;
+-import cz.habarta.typescript.generator.parser.TypeParser;
+-import cz.habarta.typescript.generator.util.Pair;
+-import cz.habarta.typescript.generator.util.Utils;
++import cz.habarta.typescript.generator.util.Predicate;
+ import java.io.File;
+-import java.io.InputStream;
+ import java.lang.annotation.Annotation;
+-import java.lang.annotation.ElementType;
+-import java.lang.annotation.Retention;
+-import java.lang.annotation.RetentionPolicy;
+-import java.lang.annotation.Target;
+-import java.lang.reflect.TypeVariable;
+-import java.net.URL;
+-import java.net.URLClassLoader;
+-import java.util.ArrayList;
+-import java.util.Arrays;
+-import java.util.Collections;
+-import java.util.LinkedHashMap;
+-import java.util.LinkedHashSet;
+-import java.util.List;
+-import java.util.Locale;
+-import java.util.Map;
+-import java.util.Objects;
+-import java.util.Set;
+-import java.util.function.Predicate;
+-import java.util.regex.Matcher;
++import java.util.*;
+ import java.util.regex.Pattern;
+-import java.util.stream.Collectors;
+-import java.util.stream.Stream;
+
+
+ /**
+- * See cz.habarta.typescript.generator.maven.GenerateMojo
++ * @see cz.habarta.typescript.generator.maven.GenerateMojo
+ * @see README.md on GitHub or in project root directory
+ * @see Wiki on GitHub
+ */
+@@ -53,173 +22,30 @@ public class Settings {
+ public TypeScriptOutputKind outputKind = null;
+ public String module = null;
+ public String namespace = null;
+- public boolean mapPackagesToNamespaces = false;
+- public String umdNamespace = null;
+- public List moduleDependencies = new ArrayList<>();
+- private LoadedModuleDependencies loadedModuleDependencies = null;
+ public JsonLibrary jsonLibrary = null;
+- public Jackson2ConfigurationResolved jackson2Configuration = null;
+- public GsonConfiguration gsonConfiguration = null;
+- public JsonbConfiguration jsonbConfiguration = null;
+- public List additionalDataLibraries = new ArrayList<>();
+- private LoadedDataLibraries loadedDataLibrariesClasses = null;
+ private Predicate excludeFilter = null;
+- public OptionalProperties optionalProperties; // default is OptionalProperties.useSpecifiedAnnotations
+- public OptionalPropertiesDeclaration optionalPropertiesDeclaration; // default is OptionalPropertiesDeclaration.questionMark
+- public NullabilityDefinition nullabilityDefinition; // default is NullabilityDefinition.nullInlineUnion
+- private TypeParser typeParser = null;
+- public boolean declarePropertiesAsReadOnly = false;
++ public boolean declarePropertiesAsOptional = false;
+ public String removeTypeNamePrefix = null;
+ public String removeTypeNameSuffix = null;
+ public String addTypeNamePrefix = null;
+ public String addTypeNameSuffix = null;
+ public Map customTypeNaming = new LinkedHashMap<>();
+- public String customTypeNamingFunction = null;
+- public CustomTypeNamingFunction customTypeNamingFunctionImpl = null;
+ public List referencedFiles = new ArrayList<>();
+ public List importDeclarations = new ArrayList<>();
+ public Map customTypeMappings = new LinkedHashMap<>();
+- private List validatedCustomTypeMappings = null;
+- public Map customTypeAliases = new LinkedHashMap<>();
+- private List validatedCustomTypeAliases = null;
+ public DateMapping mapDate; // default is DateMapping.asDate
+- public MapMapping mapMap; // default is MapMapping.asIndexedArray
+ public EnumMapping mapEnum; // default is EnumMapping.asUnion
+- public IdentifierCasing enumMemberCasing; // default is IdentifierCasing.keepOriginal
+- public boolean nonConstEnums = false;
+- public List> nonConstEnumAnnotations = new ArrayList<>();
+- public ClassMapping mapClasses; // default is ClassMapping.asInterfaces
+- public List mapClassesAsClassesPatterns;
+- private Predicate mapClassesAsClassesFilter = null;
+- public boolean generateConstructors = false;
+- public List> disableTaggedUnionAnnotations = new ArrayList<>();
+ public boolean disableTaggedUnions = false;
+- public boolean generateReadonlyAndWriteonlyJSDocTags = false;
+- public boolean ignoreSwaggerAnnotations = false;
+- public boolean generateJaxrsApplicationInterface = false;
+- public boolean generateJaxrsApplicationClient = false;
+- public boolean generateSpringApplicationInterface = false;
+- public boolean generateSpringApplicationClient = false;
+- public boolean scanSpringApplication;
+- public RestNamespacing restNamespacing;
+- public Class extends Annotation> restNamespacingAnnotation = null;
+- public String restNamespacingAnnotationElement; // default is "value"
+- public String restResponseType = null;
+- public String restOptionsType = null;
+- public boolean restOptionsTypeIsGeneric;
+- private List restApplicationParserFactories;
+ public TypeProcessor customTypeProcessor = null;
+ public boolean sortDeclarations = false;
+ public boolean sortTypeDeclarations = false;
+ public boolean noFileComment = false;
+- public boolean noTslintDisable = false;
+- public boolean noEslintDisable = false;
+- public boolean tsNoCheck = false;
+ public List javadocXmlFiles = null;
+ public List extensions = new ArrayList<>();
+ public List> includePropertyAnnotations = new ArrayList<>();
+- public List> excludePropertyAnnotations = new ArrayList<>();
+ public List> optionalAnnotations = new ArrayList<>();
+- public List> requiredAnnotations = new ArrayList<>();
+- public List> nullableAnnotations = new ArrayList<>();
+- public boolean primitivePropertiesRequired = false;
+- public boolean generateInfoJson = false;
+- public boolean generateNpmPackageJson = false;
+- public String npmName = null;
+- public String npmVersion = null;
+- public Map npmPackageDependencies = new LinkedHashMap<>();
+- public Map npmDevDependencies = new LinkedHashMap<>();
+- public Map npmPeerDependencies = new LinkedHashMap<>();
+- public String typescriptVersion = "^2.4";
+- public String npmTypescriptVersion = null;
+- public String npmBuildScript = null;
+- public boolean jackson2ModuleDiscovery = false;
+- public List> jackson2Modules = new ArrayList<>();
+- public ClassLoader classLoader = null;
++ public boolean displaySerializerWarning = true;
+
+- private boolean defaultStringEnumsOverriddenByExtension = false;
+-
+- public static class ConfiguredExtension {
+- public String className;
+- public Map configuration;
+- }
+-
+- public static class CustomTypeMapping {
+- public final Class> rawClass;
+- public final boolean matchSubclasses;
+- public final GenericName javaType;
+- public final GenericName tsType;
+-
+- public CustomTypeMapping(Class> rawClass, boolean matchSubclasses, GenericName javaType, GenericName tsType) {
+- this.rawClass = rawClass;
+- this.matchSubclasses = matchSubclasses;
+- this.javaType = javaType;
+- this.tsType = tsType;
+- }
+-
+- @Override
+- public String toString() {
+- return Utils.objectToString(this);
+- }
+- }
+-
+- public static class CustomTypeAlias {
+- public final GenericName tsType;
+- public final String tsDefinition;
+-
+- public CustomTypeAlias(GenericName tsType, String tsDefinition) {
+- this.tsType = tsType;
+- this.tsDefinition = tsDefinition;
+- }
+- }
+-
+- public static class GenericName {
+- public final String rawName;
+- public final List typeParameters;
+-
+- public GenericName(String rawName, List typeParameters) {
+- this.rawName = Objects.requireNonNull(rawName);
+- this.typeParameters = typeParameters;
+- }
+-
+- public int indexOfTypeParameter(String typeParameter) {
+- return typeParameters != null ? typeParameters.indexOf(typeParameter) : -1;
+- }
+- }
+-
+- private static class TypeScriptGeneratorURLClassLoader extends URLClassLoader {
+-
+- private final String name;
+-
+- public TypeScriptGeneratorURLClassLoader(String name, URL[] urls, ClassLoader parent) {
+- super(urls, parent);
+- this.name = name;
+- }
+-
+- @Override
+- public String toString() {
+- return "TsGenURLClassLoader{" + name + ", parent: " + getParent() + "}";
+- }
+-
+- }
+-
+- public static URLClassLoader createClassLoader(String name, URL[] urls, ClassLoader parent) {
+- return new TypeScriptGeneratorURLClassLoader(name, urls, parent);
+- }
+-
+- public void setStringQuotes(StringQuotes quotes) {
+- this.quotes = quotes == StringQuotes.singleQuotes ? "'" : "\"";
+- }
+-
+- public void setIndentString(String indentString) {
+- this.indentString = indentString != null ? indentString : " ";
+- }
+-
+- public void setJackson2Configuration(ClassLoader classLoader, Jackson2Configuration configuration) {
+- if (configuration != null) {
+- jackson2Configuration = Jackson2ConfigurationResolved.from(configuration, classLoader);
+- }
+- }
+
+ public void loadCustomTypeProcessor(ClassLoader classLoader, String customTypeProcessor) {
+ if (customTypeProcessor != null) {
+@@ -227,357 +53,62 @@ public class Settings {
+ }
+ }
+
+- public void loadExtensions(ClassLoader classLoader, List extensions, List extensionsWithConfiguration) {
+- this.extensions = new ArrayList<>();
+- this.extensions.addAll(loadInstances(classLoader, extensions, EmitterExtension.class));
+- if (extensionsWithConfiguration != null) {
+- for (ConfiguredExtension configuredExtension : extensionsWithConfiguration) {
+- final EmitterExtension emitterExtension = loadInstance(classLoader, configuredExtension.className, EmitterExtension.class);
+- if (emitterExtension instanceof Extension) {
+- final Extension extension = (Extension) emitterExtension;
+- extension.setConfiguration(Utils.mapFromNullable(configuredExtension.configuration));
+- }
+- this.extensions.add(emitterExtension);
+- }
++ public void loadExtensions(ClassLoader classLoader, List extensions) {
++ if (extensions != null) {
++ this.extensions = loadInstances(classLoader, extensions, EmitterExtension.class);
+ }
+ }
+
+- public void loadNonConstEnumAnnotations(ClassLoader classLoader, List stringAnnotations) {
+- this.nonConstEnumAnnotations = loadClasses(classLoader, stringAnnotations, Annotation.class);
+- }
+-
+ public void loadIncludePropertyAnnotations(ClassLoader classLoader, List includePropertyAnnotations) {
+- this.includePropertyAnnotations = loadClasses(classLoader, includePropertyAnnotations, Annotation.class);
+- }
+-
+- public void loadExcludePropertyAnnotations(ClassLoader classLoader, List excludePropertyAnnotations) {
+- this.excludePropertyAnnotations = loadClasses(classLoader, excludePropertyAnnotations, Annotation.class);
++ if (includePropertyAnnotations != null) {
++ this.includePropertyAnnotations = loadClasses(classLoader, includePropertyAnnotations, Annotation.class);
++ }
+ }
+
+ public void loadOptionalAnnotations(ClassLoader classLoader, List optionalAnnotations) {
+- this.optionalAnnotations = loadClasses(classLoader, optionalAnnotations, Annotation.class);
+- }
+-
+- public void loadRequiredAnnotations(ClassLoader classLoader, List requiredAnnotations) {
+- this.requiredAnnotations = loadClasses(classLoader, requiredAnnotations, Annotation.class);
+- }
+-
+- public void loadNullableAnnotations(ClassLoader classLoader, List nullableAnnotations) {
+- this.nullableAnnotations = loadClasses(classLoader, nullableAnnotations, Annotation.class);
+- }
+-
+- public void loadDisableTaggedUnionAnnotations(ClassLoader classLoader, List disableTaggedUnionAnnotations) {
+- this.disableTaggedUnionAnnotations = loadClasses(classLoader, disableTaggedUnionAnnotations, Annotation.class);
+- }
+-
+- public void loadJackson2Modules(ClassLoader classLoader, List jackson2Modules) {
+- this.jackson2Modules = loadClasses(classLoader, jackson2Modules, Module.class);
++ if (optionalAnnotations != null) {
++ this.optionalAnnotations = loadClasses(classLoader, optionalAnnotations, Annotation.class);
++ }
+ }
+
+- public static Map convertToMap(List items, String itemName) {
++ public static Map convertToMap(List mappings) {
+ final Map result = new LinkedHashMap<>();
+- if (items != null) {
+- for (String item : items) {
+- final String[] values = item.split(":", 2);
++ if (mappings != null) {
++ for (String mapping : mappings) {
++ final String[] values = mapping.split(":", 2);
+ if (values.length < 2) {
+- throw new RuntimeException(String.format("Invalid '%s' format: %s", itemName, item));
++ throw new RuntimeException("Invalid mapping format: " + mapping);
+ }
+ result.put(values[0].trim(), values[1].trim());
+ }
+ }
+ return result;
+ }
+-
++
+ public void validate() {
+- if (classLoader == null) {
+- classLoader = Thread.currentThread().getContextClassLoader();
+- }
+ if (outputKind == null) {
+ throw new RuntimeException("Required 'outputKind' parameter is not configured. " + seeLink());
+ }
+- if (outputKind == TypeScriptOutputKind.ambientModule && outputFileType == TypeScriptFileType.implementationFile) {
+- throw new RuntimeException("Ambient modules are not supported in implementation files. " + seeLink());
+- }
+ if (outputKind == TypeScriptOutputKind.ambientModule && module == null) {
+ throw new RuntimeException("'module' parameter must be specified for ambient module. " + seeLink());
+ }
+ if (outputKind != TypeScriptOutputKind.ambientModule && module != null) {
+ throw new RuntimeException("'module' parameter is only applicable to ambient modules. " + seeLink());
+ }
+- if (outputKind != TypeScriptOutputKind.module && umdNamespace != null) {
+- throw new RuntimeException("'umdNamespace' parameter is only applicable to modules. " + seeLink());
+- }
+- if (outputFileType == TypeScriptFileType.implementationFile && umdNamespace != null) {
+- throw new RuntimeException("'umdNamespace' parameter is not applicable to implementation files. " + seeLink());
+- }
+- if (umdNamespace != null && !ModelCompiler.isValidIdentifierName(umdNamespace)) {
+- throw new RuntimeException("Value of 'umdNamespace' parameter is not valid identifier: " + umdNamespace + ". " + seeLink());
++ if (outputKind == TypeScriptOutputKind.ambientModule && outputFileType == TypeScriptFileType.implementationFile) {
++ throw new RuntimeException("Ambient modules are not supported in implementation files. " + seeLink());
+ }
+ if (jsonLibrary == null) {
+ throw new RuntimeException("Required 'jsonLibrary' parameter is not configured.");
+ }
+- if (jackson2Configuration != null && jsonLibrary != JsonLibrary.jackson2) {
+- throw new RuntimeException("'jackson2Configuration' parameter is only applicable to 'jackson2' library.");
+- }
+- if (!generateNpmPackageJson && (!npmPackageDependencies.isEmpty() || !npmDevDependencies.isEmpty() || !npmPeerDependencies.isEmpty())) {
+- throw new RuntimeException("'npmDependencies', 'npmDevDependencies' and 'npmPeerDependencies' parameters are only applicable when generating NPM 'package.json'.");
+- }
+- getValidatedCustomTypeMappings();
+- getValidatedCustomTypeAliases();
+- for (EmitterExtension extension : extensions) {
+- final String extensionName = extension.getClass().getSimpleName();
+- final DeprecationText deprecation = extension.getClass().getAnnotation(DeprecationText.class);
+- if (deprecation != null) {
+- TypeScriptGenerator.getLogger().warning(String.format("Extension '%s' is deprecated: %s", extensionName, deprecation.value()));
+- }
+- final EmitterExtensionFeatures features = extension.getFeatures();
+- if (features.generatesRuntimeCode && outputFileType != TypeScriptFileType.implementationFile) {
+- throw new RuntimeException(String.format("Extension '%s' generates runtime code but 'outputFileType' parameter is not set to 'implementationFile'.", extensionName));
+- }
+- if (features.generatesModuleCode && outputKind != TypeScriptOutputKind.module) {
+- throw new RuntimeException(String.format("Extension '%s' generates code as module but 'outputKind' parameter is not set to 'module'.", extensionName));
+- }
+- if (!features.worksWithPackagesMappedToNamespaces && mapPackagesToNamespaces) {
+- throw new RuntimeException(String.format("Extension '%s' doesn't work with 'mapPackagesToNamespaces' parameter.", extensionName));
+- }
+- if (features.generatesJaxrsApplicationClient) {
+- reportConfigurationChange(extensionName, "generateJaxrsApplicationClient", "true");
+- generateJaxrsApplicationClient = true;
+- }
+- if (features.restResponseType != null) {
+- reportConfigurationChange(extensionName, "restResponseType", features.restResponseType);
+- restResponseType = features.restResponseType;
+- }
+- if (features.restOptionsType != null) {
+- reportConfigurationChange(extensionName, "restOptionsType", features.restOptionsType);
+- setRestOptionsType(features.restOptionsType);
+- }
+- if (features.npmPackageDependencies != null) {
+- npmPackageDependencies.putAll(features.npmPackageDependencies);
+- }
+- if (features.npmDevDependencies != null) {
+- npmDevDependencies.putAll(features.npmDevDependencies);
+- }
+- if (features.npmPeerDependencies != null) {
+- npmPeerDependencies.putAll(features.npmPeerDependencies);
+- }
+- if (features.overridesStringEnums) {
+- defaultStringEnumsOverriddenByExtension = true;
+- }
+- }
+- if (enumMemberCasing != null && mapEnum != EnumMapping.asEnum && mapEnum != EnumMapping.asNumberBasedEnum) {
+- throw new RuntimeException("'enumMemberCasing' parameter can only be used when 'mapEnum' parameter is set to 'asEnum' or 'asNumberBasedEnum'.");
+- }
+- if ((nonConstEnums || !nonConstEnumAnnotations.isEmpty()) && outputFileType != TypeScriptFileType.implementationFile) {
+- throw new RuntimeException("Non-const enums can only be used in implementation files but 'outputFileType' parameter is not set to 'implementationFile'.");
+- }
+- if (mapClasses == ClassMapping.asClasses && outputFileType != TypeScriptFileType.implementationFile) {
+- throw new RuntimeException("'mapClasses' parameter is set to 'asClasses' which generates runtime code but 'outputFileType' parameter is not set to 'implementationFile'.");
+- }
+- if (mapClassesAsClassesPatterns != null && mapClasses != ClassMapping.asClasses) {
+- throw new RuntimeException("'mapClassesAsClassesPatterns' parameter can only be used when 'mapClasses' parameter is set to 'asClasses'.");
+- }
+- if (generateConstructors && mapClasses != ClassMapping.asClasses) {
+- throw new RuntimeException("'generateConstructors' parameter can only be used when 'mapClasses' parameter is set to 'asClasses'.");
+- }
+- checkAnnotationsHaveRuntimeRetention(this.nonConstEnumAnnotations);
+- checkAnnotationsHaveRuntimeRetention(this.disableTaggedUnionAnnotations);
+- checkAnnotationHasRuntimeRetention(this.restNamespacingAnnotation);
+- checkAnnotationsHaveRuntimeRetention(this.includePropertyAnnotations);
+- checkAnnotationsHaveRuntimeRetention(this.excludePropertyAnnotations);
+- checkAnnotationsHaveRuntimeRetention(this.optionalAnnotations);
+- checkAnnotationsHaveRuntimeRetention(this.requiredAnnotations);
+- checkAnnotationsHaveRuntimeRetention(this.nullableAnnotations);
+- for (Class extends Annotation> annotation : optionalAnnotations) {
+- final Target target = annotation.getAnnotation(Target.class);
+- final List elementTypes = target != null ? Arrays.asList(target.value()) : Arrays.asList();
+- if (elementTypes.contains(ElementType.TYPE_PARAMETER) || elementTypes.contains(ElementType.TYPE_USE)) {
+- TypeScriptGenerator.getLogger().info(String.format(
+- "Suggestion: annotation '%s' supports 'TYPE_PARAMETER' or 'TYPE_USE' target. Consider using 'nullableAnnotations' parameter instead of 'optionalAnnotations'.",
+- annotation.getName()));
+- }
+- }
+- if (!optionalAnnotations.isEmpty() && !requiredAnnotations.isEmpty()) {
+- throw new RuntimeException("Only one of 'optionalAnnotations' and 'requiredAnnotations' can be used at the same time.");
+- }
+- if (primitivePropertiesRequired && requiredAnnotations.isEmpty()) {
+- throw new RuntimeException("'primitivePropertiesRequired' parameter can only be used with 'requiredAnnotations' parameter.");
+- }
+- for (Class extends Annotation> annotation : nullableAnnotations) {
+- final Target target = annotation.getAnnotation(Target.class);
+- final List elementTypes = target != null ? Arrays.asList(target.value()) : Arrays.asList();
+- if (!elementTypes.contains(ElementType.TYPE_PARAMETER) && !elementTypes.contains(ElementType.TYPE_USE)) {
+- throw new RuntimeException(String.format(
+- "'%s' annotation cannot be used as nullable annotation because it doesn't have 'TYPE_PARAMETER' or 'TYPE_USE' target.",
+- annotation.getName()));
+- }
+- }
+- if (generateJaxrsApplicationClient && outputFileType != TypeScriptFileType.implementationFile) {
+- throw new RuntimeException("'generateJaxrsApplicationClient' can only be used when generating implementation file ('outputFileType' parameter is 'implementationFile').");
+- }
+- if (generateSpringApplicationClient && outputFileType != TypeScriptFileType.implementationFile) {
+- throw new RuntimeException("'generateSpringApplicationClient' can only be used when generating implementation file ('outputFileType' parameter is 'implementationFile').");
+- }
+- if (restNamespacing != null && !isGenerateRest()) {
+- throw new RuntimeException("'restNamespacing' parameter can only be used when generating REST client or interface.");
+- }
+- if (restNamespacingAnnotation != null && restNamespacing != RestNamespacing.byAnnotation) {
+- throw new RuntimeException("'restNamespacingAnnotation' parameter can only be used when 'restNamespacing' parameter is set to 'byAnnotation'.");
+- }
+- if (restNamespacingAnnotation == null && restNamespacing == RestNamespacing.byAnnotation) {
+- throw new RuntimeException("'restNamespacingAnnotation' must be specified when 'restNamespacing' parameter is set to 'byAnnotation'.");
+- }
+- if (restResponseType != null && !isGenerateRest()) {
+- throw new RuntimeException("'restResponseType' parameter can only be used when generating REST client or interface.");
+- }
+- if (restOptionsType != null && !isGenerateRest()) {
+- throw new RuntimeException("'restOptionsType' parameter can only be used when generating REST client or interface.");
+- }
+- if (generateInfoJson && (outputKind != TypeScriptOutputKind.module && outputKind != TypeScriptOutputKind.global)) {
+- throw new RuntimeException("'generateInfoJson' can only be used when 'outputKind' parameter is 'module' or 'global'.");
+- }
+- if (generateNpmPackageJson && outputKind != TypeScriptOutputKind.module) {
+- throw new RuntimeException("'generateNpmPackageJson' can only be used when generating proper module ('outputKind' parameter is 'module').");
+- }
+- if (generateNpmPackageJson) {
+- if (npmName == null || npmVersion == null) {
+- throw new RuntimeException("'npmName' and 'npmVersion' must be specified when generating NPM 'package.json'.");
+- }
+- }
+- if (!generateNpmPackageJson) {
+- if (npmName != null || npmVersion != null) {
+- throw new RuntimeException("'npmName' and 'npmVersion' is only applicable when generating NPM 'package.json'.");
+- }
+- if (npmTypescriptVersion != null) {
+- throw new RuntimeException("'npmTypescriptVersion' is only applicable when generating NPM 'package.json'.");
+- }
+- if (npmBuildScript != null) {
+- throw new RuntimeException("'npmBuildScript' is only applicable when generating NPM 'package.json'.");
+- }
+- }
+- if (npmTypescriptVersion != null && outputFileType != TypeScriptFileType.implementationFile) {
+- throw new RuntimeException("'npmTypescriptVersion' can only be used when generating implementation file ('outputFileType' parameter is 'implementationFile').");
+- }
+- if (npmBuildScript != null && outputFileType != TypeScriptFileType.implementationFile) {
+- throw new RuntimeException("'npmBuildScript' can only be used when generating implementation file ('outputFileType' parameter is 'implementationFile').");
+- }
+- getModuleDependencies();
+- getLoadedDataLibraries();
+- }
+-
+- public NullabilityDefinition getNullabilityDefinition() {
+- return nullabilityDefinition != null ? nullabilityDefinition : NullabilityDefinition.nullInlineUnion;
+- }
+-
+- public TypeParser getTypeParser() {
+- if (typeParser == null) {
+- typeParser = new TypeParser(nullableAnnotations);
+- }
+- return typeParser;
+- }
+-
+- public List getValidatedCustomTypeMappings() {
+- if (validatedCustomTypeMappings == null) {
+- validatedCustomTypeMappings = Utils.concat(
+- validateCustomTypeMappings(customTypeMappings, false),
+- getLoadedDataLibraries().typeMappings);
+- }
+- return validatedCustomTypeMappings;
+- }
+-
+- private List validateCustomTypeMappings(Map customTypeMappings, boolean matchSubclasses) {
+- final List mappings = new ArrayList<>();
+- for (Map.Entry entry : customTypeMappings.entrySet()) {
+- final String javaName = entry.getKey();
+- final String tsName = entry.getValue();
+- try {
+- final GenericName genericJavaName = parseGenericName(javaName);
+- final GenericName genericTsName = parseGenericName(tsName);
+- validateTypeParameters(genericJavaName.typeParameters);
+- validateTypeParameters(genericTsName.typeParameters);
+- final Class> cls = loadClass(classLoader, genericJavaName.rawName, null);
+- final int required = cls.getTypeParameters().length;
+- final int specified = genericJavaName.typeParameters != null ? genericJavaName.typeParameters.size() : 0;
+- if (specified != required) {
+- final String parameters = Stream.of(cls.getTypeParameters())
+- .map(TypeVariable::getName)
+- .collect(Collectors.joining(", "));
+- final String signature = cls.getName() + (parameters.isEmpty() ? "" : "<" + parameters + ">");
+- throw new RuntimeException(String.format(
+- "Wrong number of specified generic parameters, required: %s, found: %s. Correct format is: '%s'",
+- required, specified, signature));
+- }
+- mappings.add(new CustomTypeMapping(cls, matchSubclasses, genericJavaName, genericTsName));
+- } catch (Exception e) {
+- throw new RuntimeException(String.format("Failed to parse configured custom type mapping '%s:%s': %s", javaName, tsName, e.getMessage()), e);
+- }
+- }
+- return mappings;
+- }
+-
+- public List getValidatedCustomTypeAliases() {
+- if (validatedCustomTypeAliases == null) {
+- validatedCustomTypeAliases = Utils.concat(
+- validateCustomTypeAliases(customTypeAliases),
+- getLoadedDataLibraries().typeAliases);
+- }
+- return validatedCustomTypeAliases;
+- }
+-
+- public List validateCustomTypeAliases(Map customTypeAliases) {
+- final List aliases = new ArrayList<>();
+- for (Map.Entry entry : customTypeAliases.entrySet()) {
+- final String tsName = entry.getKey();
+- final String tsDefinition = entry.getValue();
+- try {
+- final GenericName genericTsName = parseGenericName(tsName);
+- if (!ModelCompiler.isValidIdentifierName(genericTsName.rawName)) {
+- throw new RuntimeException(String.format("Invalid identifier: '%s'", genericTsName.rawName));
++ if (outputFileType != TypeScriptFileType.implementationFile) {
++ for (EmitterExtension emitterExtension : extensions) {
++ if (emitterExtension.getFeatures().generatesRuntimeCode) {
++ throw new RuntimeException(String.format("Extension '%s' generates runtime code but 'outputFileType' parameter is not set to 'implementationFile'.",
++ emitterExtension.getClass().getSimpleName()));
+ }
+- validateTypeParameters(genericTsName.typeParameters);
+- aliases.add(new CustomTypeAlias(genericTsName, tsDefinition));
+- } catch (Exception e) {
+- throw new RuntimeException(String.format("Failed to parse configured custom type alias '%s:%s': %s", tsName, tsDefinition, e.getMessage()), e);
+ }
+ }
+- return aliases;
+- }
+-
+- private static GenericName parseGenericName(String name) {
+- // Class
+- // Class[T1, T2]
+- final Matcher matcher = Pattern.compile("([^<\\[]+)(<|\\[)([^>\\]]+)(>|\\])").matcher(name);
+- final String rawName;
+- final List typeParameters;
+- if (matcher.matches()) { // is generic?
+- rawName = matcher.group(1);
+- typeParameters = Stream.of(matcher.group(3).split(","))
+- .map(String::trim)
+- .collect(Collectors.toList());
+- } else {
+- rawName = name;
+- typeParameters = null;
+- }
+- return new GenericName(rawName, typeParameters);
+- }
+-
+- private static void validateTypeParameters(List typeParameters) {
+- if (typeParameters == null) {
+- return;
+- }
+- for (String typeParameter : typeParameters) {
+- if (!ModelCompiler.isValidIdentifierName(typeParameter)) {
+- throw new RuntimeException(String.format("Invalid generic type parameter: '%s'", typeParameter));
+- }
+- }
+- }
+-
+- private static void reportConfigurationChange(String extensionName, String parameterName, String parameterValue) {
+- TypeScriptGenerator.getLogger().info(String.format("Configuration: '%s' extension set '%s' parameter to '%s'", extensionName, parameterName, parameterValue));
+- }
+-
+- public String getExtension() {
+- return outputFileType == TypeScriptFileType.implementationFile ? ".ts" : ".d.ts";
+ }
+
+ public void validateFileName(File outputFile) {
+@@ -589,76 +120,6 @@ public class Settings {
+ }
+ }
+
+- public String getDefaultNpmVersion() {
+- return "1.0.0";
+- }
+-
+- public LoadedModuleDependencies getModuleDependencies() {
+- if (loadedModuleDependencies == null) {
+- loadedModuleDependencies = new LoadedModuleDependencies(this, moduleDependencies);
+- }
+- return loadedModuleDependencies;
+- }
+-
+- public LoadedDataLibraries getLoadedDataLibraries() {
+- if (loadedDataLibrariesClasses == null) {
+- loadedDataLibrariesClasses = loadDataLibrariesClasses();
+- }
+- return loadedDataLibrariesClasses;
+- }
+-
+- private LoadedDataLibraries loadDataLibrariesClasses() {
+- if (additionalDataLibraries == null) {
+- return new LoadedDataLibraries();
+- }
+- final List loaded = new ArrayList<>();
+- final ObjectMapper objectMapper = Utils.getObjectMapper();
+- objectMapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
+- for (String library : additionalDataLibraries) {
+- final String resource = "datalibrary/" + library + ".json";
+- TypeScriptGenerator.getLogger().verbose("Loading resource " + resource);
+- final InputStream inputStream = classLoader.getResourceAsStream(resource);
+- if (inputStream == null) {
+- throw new RuntimeException("Resource not found: " + resource);
+- }
+- final DataLibraryJson dataLibrary = Utils.loadJson(objectMapper, inputStream, DataLibraryJson.class);
+- final Map typeMappings = Utils.listFromNullable(dataLibrary.classMappings).stream()
+- .filter(mapping -> mapping.customType != null)
+- .collect(Utils.toMap(
+- mapping -> mapping.className,
+- mapping -> mapping.customType
+- ));
+- final Map typeAliases = Utils.listFromNullable(dataLibrary.typeAliases).stream()
+- .collect(Utils.toMap(
+- alias -> alias.name,
+- alias -> alias.definition
+- ));
+- loaded.add(new LoadedDataLibraries(
+- loadDataLibraryClasses(dataLibrary, DataLibraryJson.SemanticType.String),
+- loadDataLibraryClasses(dataLibrary, DataLibraryJson.SemanticType.Number),
+- loadDataLibraryClasses(dataLibrary, DataLibraryJson.SemanticType.Boolean),
+- loadDataLibraryClasses(dataLibrary, DataLibraryJson.SemanticType.Date),
+- loadDataLibraryClasses(dataLibrary, DataLibraryJson.SemanticType.Any),
+- loadDataLibraryClasses(dataLibrary, DataLibraryJson.SemanticType.Void),
+- loadDataLibraryClasses(dataLibrary, DataLibraryJson.SemanticType.List),
+- loadDataLibraryClasses(dataLibrary, DataLibraryJson.SemanticType.Map),
+- loadDataLibraryClasses(dataLibrary, DataLibraryJson.SemanticType.Optional),
+- loadDataLibraryClasses(dataLibrary, DataLibraryJson.SemanticType.Wrapper),
+- validateCustomTypeMappings(typeMappings, true),
+- validateCustomTypeAliases(typeAliases)
+- ));
+- }
+- return LoadedDataLibraries.join(loaded);
+- }
+-
+- private List> loadDataLibraryClasses(DataLibraryJson dataLibrary, DataLibraryJson.SemanticType semanticType) {
+- final List classNames = dataLibrary.classMappings.stream()
+- .filter(mapping -> mapping.semanticType == semanticType)
+- .map(mapping -> mapping.className)
+- .collect(Collectors.toList());
+- return loadClasses(classLoader, classNames, null);
+- }
+-
+ public Predicate getExcludeFilter() {
+ if (excludeFilter == null) {
+ setExcludeFilter(null, null);
+@@ -672,104 +133,22 @@ public class Settings {
+
+ public static Predicate createExcludeFilter(List excludedClasses, List excludedClassPatterns) {
+ final Set names = new LinkedHashSet<>(excludedClasses != null ? excludedClasses : Collections.emptyList());
+- names.add("java.lang.Record");
+- final List patterns = Utils.globsToRegexps(excludedClassPatterns != null ? excludedClassPatterns : Collections.