uncommitted - jodd

Ready changes

Summary

Import uploads missing from VCS:

Diff

diff --git a/.pc/.quilt_patches b/.pc/.quilt_patches
new file mode 100644
index 0000000..6857a8d
--- /dev/null
+++ b/.pc/.quilt_patches
@@ -0,0 +1 @@
+debian/patches
diff --git a/.pc/.quilt_series b/.pc/.quilt_series
new file mode 100644
index 0000000..c206706
--- /dev/null
+++ b/.pc/.quilt_series
@@ -0,0 +1 @@
+series
diff --git a/.pc/.version b/.pc/.version
new file mode 100644
index 0000000..0cfbf08
--- /dev/null
+++ b/.pc/.version
@@ -0,0 +1 @@
+2
diff --git a/.pc/01-disable-coverage-report.patch/build.gradle b/.pc/01-disable-coverage-report.patch/build.gradle
new file mode 100644
index 0000000..2911c99
--- /dev/null
+++ b/.pc/01-disable-coverage-report.patch/build.gradle
@@ -0,0 +1,608 @@
+// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+plugins {
+	id "com.jfrog.bintray" version "1.6"
+}
+
+description = '''
+
+	Jodd is an open-source Java utility library and set of micro frameworks.
+	Jodd tools enriches JDK with many powerful and feature rich utilities.
+	It helps with everyday task, makes code more robust and reliable.
+	Jodd frameworks is set of lightweight application frameworks, compact yet powerful.
+	Designed following the CoC, DRY and SCS principles, it makes development
+	simple, but not simpler.
+'''
+
+//version = '3.8.6-SNAPSHOT'
+//version = '3.8.2-' + date()
+version = '3.8.6'
+
+// --- properties -------------------------------------------------------------
+
+ext {
+	gradleScriptDir = "${rootProject.projectDir}/gradle"
+
+	// libraries
+
+	tomcatVersion = '7.0.47'
+	jacocoVersion = '0.7.1.201405082137'
+
+	lib = [
+		mail:			[
+							'javax.mail:javax.mail-api:1.5.6',
+							'com.sun.mail:javax.mail:1.5.6'
+						],
+		activation:		'javax.activation:activation:1.1.1',
+		servlet:		'javax.servlet:javax.servlet-api:3.0.1',
+		jsp:			'javax.servlet.jsp:jsp-api:2.2',
+
+		log_slf4j:			'org.slf4j:slf4j-api:[1.7,1.8)',
+		log_slf4j_simple:	'org.slf4j:slf4j-simple:[1.7,1.8)',
+		log_logback_core: 	'ch.qos.logback:logback-core:[1.2,1.3)',
+		log_logback_classic: 'ch.qos.logback:logback-classic:[1.2,1.3)',
+		log_jcl:        	'commons-logging:commons-logging:[1.2,1.3)',
+		log_log4j2:		  'org.apache.logging.log4j:log4j-api:[2.8,2.9)',
+		log_log4j2_core:		  'org.apache.logging.log4j:log4j-core:[2.8,2.9)',
+
+//		asm:			'org.ow2.asm:asm:4.1',
+
+		junit: 			'junit:junit:4.12',
+		hamcrest:       'org.hamcrest:hamcrest-core:1.3',
+		mockito:		'org.mockito:mockito-all:1.10.19',
+		powermock:		[
+							'org.powermock:powermock-module-junit4:1.6.4',
+							'org.powermock:powermock-api-mockito:1.6.4'
+						],
+		hsqldb:			'org.hsqldb:hsqldb:2.2.9',
+		h2db:			'com.h2database:h2:1.1.111',
+		postgresql:		'org.postgresql:postgresql:9.4-1201-jdbc41',
+		mysql:			'mysql:mysql-connector-java:5.1.37',
+		winstone:		'net.sourceforge.winstone:winstone:0.9.10',
+		greenmail:		'com.icegreen:greenmail:1.5.2',
+		//el_api:		'javax.el:el-api:2.2',
+		el_api:			"org.apache.tomcat:tomcat-jasper:${tomcatVersion}",
+		tomcat_embed:	[
+							"org.apache.tomcat:tomcat-jasper:${tomcatVersion}",
+							"org.apache.tomcat:tomcat-jasper-el:${tomcatVersion}",
+							"org.apache.tomcat:tomcat-catalina:${tomcatVersion}",
+							"org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}",
+							"org.apache.tomcat.embed:tomcat-embed-jasper:${tomcatVersion}",
+							"org.apache.tomcat.embed:tomcat-embed-logging-juli:${tomcatVersion}",
+							"org.apache.tomcat.embed:tomcat-embed-logging-log4j:${tomcatVersion}"],
+		jacoco_agent:	"org.jacoco:org.jacoco.agent:${jacocoVersion}:runtime@jar",
+		jacoco_ant:		"org.jacoco:org.jacoco.ant:${jacocoVersion}",
+		mockserver:		'org.mock-server:mockserver-netty:3.10.4',
+	]
+
+	commonManifest = [
+		'Debug-Info' : 'on',
+		'Built-By' : 'jodd.org',
+		'Bundle-License' : 'http://jodd.org/license.html',
+		'Bundle-Vendor' : 'Jodd',
+		'Bundle-DocURL' : 'http://jodd.org',
+		'Implementation-Version': version,
+		'Implementation-Vendor': 'Jodd Team'
+	]
+}
+
+
+
+// --- modules sets -----------------------------------------------------------
+
+allprojects {
+	version = rootProject.version
+
+	ext.isSnapshot = version.contains("-");
+	ext.isSubproject = project.parent != null
+
+	repositories {
+		jcenter()
+		mavenCentral()
+	}
+}
+
+def javaModules() {
+	subprojects.findAll {
+		it.name.contains('jodd-') &&
+		!it.name.equals('jodd-bom') &&
+		!it.name.equals('jodd-all')
+	}
+}
+
+def javaBundleModules() {
+	rootProject.subprojects.findAll {
+		it.name.contains('jodd-') &&
+		!it.name.equals('jodd-swingspy') &&
+		!it.name.equals('jodd-joy') &&
+		!it.name.equals('jodd-bom') &&
+		!it.name.equals('jodd-all')
+	}
+}
+
+def javaMobileModules() {
+	rootProject.subprojects.findAll {
+		it.name.equals('jodd-core') ||
+		it.name.equals('jodd-bean') ||
+		it.name.equals('jodd-props')
+	}
+}
+
+
+// --- configuration ----------------------------------------------------------
+
+apply plugin: 'idea'
+apply plugin: 'eclipse'
+
+// exclude folders from IDEA project
+idea {
+	module {
+		excludeDirs += file('.idea')
+	}
+}
+
+configure(javaModules()) {
+	apply plugin: 'java'
+	apply plugin: 'osgi'
+	apply plugin: 'maven'
+	apply plugin: 'signing'
+	apply plugin: 'idea'
+	apply plugin: 'eclipse'
+	apply from: "${gradleScriptDir}/provided.gradle"
+	apply from: "${gradleScriptDir}/publish-maven.gradle"
+
+	group = 'org.jodd'
+
+	// bintray
+
+	apply plugin: 'com.jfrog.bintray'
+	apply from: "${gradleScriptDir}/bintray.gradle"
+
+	configurations {
+		published
+	}
+
+	// compile
+
+	compileJava {
+		sourceCompatibility = '1.8'
+		targetCompatibility = '1.8'
+	}
+
+	if (JavaVersion.current() != JavaVersion.VERSION_1_8) {
+		throw new GradleException("This build must be run with Java 8.")
+	}
+
+	tasks.withType(JavaCompile) {
+		options.encoding = 'UTF-8'
+		options.compilerArgs << "-Xlint:-options"
+		options.incremental = true
+		//configurations.compile.transitive = false
+	}
+	tasks.withType(Javadoc) {
+		options.addStringOption('Xdoclint:none', '-quiet')
+	}
+	compileJava.options.debug = true
+	compileJava.options.fork = true
+
+	task sourcesJar(type: Jar, dependsOn: classes) {
+		classifier = 'sources'
+		from sourceSets.main.allSource
+	}
+	task javadocJar(type: Jar, dependsOn: javadoc) {
+		classifier = 'javadoc'
+		from javadoc.destinationDir
+	}
+
+	// integration tests + performances
+
+	sourceSets {
+		perf {
+			java {
+				compileClasspath += main.output
+				runtimeClasspath += main.output
+			}
+		}
+		testInt {
+			java {
+				compileClasspath += main.output
+				compileClasspath += test.output
+				runtimeClasspath += main.output
+				runtimeClasspath += test.output
+			}
+		}
+	}
+
+	configurations {
+		testIntCompile.extendsFrom testCompile
+		testIntRuntime.extendsFrom testRuntime
+
+		perfCompile.extendsFrom testCompile
+		perfRuntime.extendsFrom testRuntime
+	}
+
+	dependencies {
+		perfCompile 'org.openjdk.jmh:jmh-core:1.15'
+		perfCompile 'org.openjdk.jmh:jmh-generator-annprocess:1.15'
+	}
+
+	idea {
+		module {
+			// DONT: use separate output folders for idea, don't mess with gradle
+			inheritOutputDirs false
+
+			//
+			// put our custom test dependencies onto IDEA's TEST scope
+			// IT IS NOT WORKING !!!
+			// see: https://youtrack.jetbrains.com/issue/IDEA-151925
+			// see: fixIdeaTestScopes.sh
+			//
+			//scopes.TEST.plus += [ configurations.testIntCompile ]
+			//scopes.TEST.plus += [ configurations.perfCompile ]
+		}
+	}
+
+	task perf(type:JavaExec) {
+		group 'Application'
+		description 'Execute benchmarks.'
+
+		main 'org.openjdk.jmh.Main'
+		classpath = sourceSets.perf.runtimeClasspath
+	}
+
+	task testIntegration(type: Test, dependsOn: jar) {
+		group 'Verification'
+		description 'Runs the integration tests.'
+		testLogging {
+			showStandardStreams = true
+		}
+
+		testClassesDir = sourceSets.testInt.output.classesDir
+		classpath = sourceSets.testInt.runtimeClasspath
+		systemProperties['jar.path'] = jar.archivePath
+	}
+
+	// artifacts
+
+	artifacts {
+		archives jar
+		archives sourcesJar
+		archives javadocJar
+
+		published jar
+		published sourcesJar
+		published javadocJar
+	}
+
+	jar {
+		afterEvaluate {
+			manifest.attributes << commonManifest
+			manifest {
+				name = project.moduleName
+				version = project.version
+				description = project.moduleDescription
+				attributes 'Implementation-Title': project.moduleName
+			}
+		}
+	}
+
+	javadoc {
+		exclude '**/asm5/**'
+		afterEvaluate {
+			configure(options) {
+				windowTitle 'Jodd API Documentation'
+				docTitle "$project.moduleName $project.version API Documentation"
+				bottom = 'Copyright &#169; 2003-2013 <a href="http://jodd.org">Jodd Team</a>'
+				breakIterator = true
+				author = false
+				encoding = 'UTF-8'
+				docEncoding = 'UTF-8'
+				stylesheetFile = file('src/main/javadoc/jodd.css')
+				source = '1.8'
+				failOnError = false
+			}
+		}
+	}
+
+	// JaCoCo
+
+	configurations {
+		jacoco
+	}
+
+	dependencies {
+		jacoco lib.jacoco_agent
+	}
+
+
+	// Test: configure all tests, including integration
+
+	tasks.withType(Test) { testTask ->
+		configure(testTask) {
+			beforeTest { descriptor ->
+				logger.lifecycle("\t" + descriptor)
+			}
+
+			reports.html.enabled = true
+
+			systemProperties = System.properties
+
+			// we need to exclude certain classes as jacoco changes them and may influence testing results
+			List<String> excludes = ['*Test*', '*.?', '*Foo*', '*.data.*', '*.tst*', 'jodd.asm5.*', 'jodd.json.mo*', 'jodd.json.db*', '*.fixtures.*']
+
+			jvmArgs "-javaagent:${configurations.jacoco.asPath}=destfile=${project.buildDir.path}/jacoco/${testTask.name}.exec," +
+					"sessionid=HSServ,append=false,excludes=${excludes.join(':')}",
+			 	'-Djacoco=true',
+			 	'-Xms128m',
+			 	'-Xmx512m',
+			 	'-Duser.timezone=GMT'
+		}
+	}
+
+	// add fancy test output
+	tasks.withType(Test) {
+		afterSuite { desc, result ->
+			if (!desc.parent) { // will match the outermost suite
+				def output = "Results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} successes, ${result.failedTestCount} failures, ${result.skippedTestCount} skipped)"
+				def startItem = '|  ', endItem = '  |'
+				def repeatLength = startItem.length() + output.length() + endItem.length()
+				println('\n' + ('-' * repeatLength) + '\n' + startItem + output + endItem + '\n' + ('-' * repeatLength))
+			}
+		}
+	}
+
+}
+
+// --- project ----------------------------------------------------------------
+
+apply plugin: 'project-report'
+
+def javaBundleModulesSources = javaBundleModules().sourceSets.main
+
+task buildModules {
+	dependsOn javaModules().build
+}
+
+task testAll {
+	group 'Verification'
+	description 'Runs all the tests.'
+
+	//dependsOn javaTestModules().test
+
+	dependsOn javaModules().test
+	dependsOn javaModules().testIntegration
+}
+
+task clean(type: Delete) {
+	group 'Project'
+	description 'Cleans everything.'
+
+	delete buildDir
+}
+
+task javadocAll(type: Javadoc) {
+	group 'Documentation'
+	description 'Builds the aggregated Java docs for all modules.'
+	dependsOn buildModules
+
+	maxMemory '512m'
+
+	source javaBundleModulesSources.allJava
+	exclude '**/asm4/**'
+	exclude '**/asm5/**'
+
+	destinationDir = file("$buildDir/reports/javadoc")
+	classpath = files(javaBundleModulesSources.compileClasspath)
+
+	configure(options) {
+		windowTitle 'Jodd API Documentation'
+		docTitle "Jodd Project $project.version API Documentation"
+		bottom = 'Copyright &#169; 2003-2016 <a href="http://jodd.org">Jodd Team</a>'
+		breakIterator = true
+		author = false
+		encoding = 'UTF-8'
+		docEncoding = 'UTF-8'
+		stylesheetFile = file('src/main/javadoc/jodd.css')
+		source = '1.9'
+	}
+
+	inputs.files(javaBundleModulesSources.allSource + javaBundleModulesSources.compileClasspath)
+	outputs.dir destinationDir
+}
+
+task testReport(type: TestReport) {
+	group 'Reporting'
+	description 'Generates tests report.'
+
+	destinationDir = file("$buildDir/reports/tests")
+
+	//reportOn javaTestModules()*.test
+
+	reportOn javaBundleModules()*.test
+	reportOn javaBundleModules()*.testIntegration
+}
+
+configurations {
+    jacoco {
+        description 'JARs required for aggregate JacocoReport task.'
+    }
+}
+dependencies {
+    jacoco lib.jacoco_ant
+}
+
+task jacocoReport() {
+	dependsOn testAll
+	group = 'Reporting'
+	description = 'Generates JaCoCo coverage reports for unit tests.'
+
+	ant.taskdef(
+			name:'jacocoreport',
+			classname: 'org.jacoco.ant.ReportTask',
+			classpath: configurations.jacoco.asPath
+	)
+
+	def coverageDir = "$buildDir/reports/coverage"
+	ant.mkdir dir: coverageDir
+
+	inputs.files(javaBundleModulesSources.allSource + javaBundleModulesSources.compileClasspath)
+	outputs.dir file(coverageDir)
+
+	doLast {
+		ant.jacocoreport {
+			executiondata {
+				javaBundleModules().each {submodule ->
+					fileset(dir: "${submodule.buildDir}/jacoco") {
+						include name: '*.exec'
+					}
+				}
+			}
+			structure(name: 'Jodd Coverage Report') {
+				javaBundleModules().each {submodule ->
+					group(name: submodule.name) {
+						classfiles {
+							fileset dir: "${submodule.sourceSets.main.output.classesDir}", excludes: "**/jodd/asm5/**"
+						}
+					}
+				}
+			}
+			html destdir: coverageDir
+			xml destfile: coverageDir + '/jodd-coverage.xml'
+		}
+	}
+}
+
+// Coveralls task should be invoked manually, since we have integration tests
+// that are executed only locally. After the release is build,
+// go to Travis Build History: https://travis-ci.org/oblac/jodd/builds
+// and select one of the jobs, then copy the job id from the URL line.
+// Paste the job id into the .coverals.yml and invoke this task
+// After few minutes, the coverage will appear here:
+// https://coveralls.io/r/oblac/jodd
+task coveralls() {
+	//dependsOn jacocoReport
+	group = 'Reporting'
+	description = 'Submits Jacoco reports to Coveralls.'
+
+	doLast {
+		List<File> targetSrcDirs = new ArrayList<File>()
+
+		for (main in javaBundleModulesSources) {
+			targetSrcDirs += main.java.srcDirs
+		}
+
+		CoverallsReporter coverallsReporter = new CoverallsReporter();
+
+		coverallsReporter.send(targetSrcDirs, file("build/reports/coverage/jodd-coverage.xml"))
+	}
+}
+
+dependencyReport {
+	projects = javaModules()
+}
+
+task bintray {
+	group 'Publishing'
+	description 'Publish all artifcats to bintray'
+
+	dependsOn javaModules().bintrayUpload
+	dependsOn(":jodd-bom:bintrayUpload")
+}
+
+// --- release ----------------------------------------------------------------
+
+task release() {
+	group 'Project'
+	description 'Builds everything for the release.'
+
+	dependsOn buildModules
+	dependsOn testAll
+	dependsOn javadocAll
+	dependsOn testReport
+	dependsOn jacocoReport
+	dependsOn ':distribution:build'
+	dependsOn projectReport
+}
+
+gradle.taskGraph.whenReady { taskGraph ->
+	if (taskGraph.hasTask(release)) {
+
+		println ""
+		println "	You are about to run the 'release' task for Jodd project!"
+		println "	This task builds distribution artifacts, but also runs"
+		println "	integration tests and generates reports (javadoc, coverage...)"
+		println "	For running integration tests you need infrastructure running;"
+		println "	please find more information here: http://jodd.org/code.html"
+		println ""
+		println "	Usually, you don't need to run this task. If you want to build"
+		println "	distribution jars and run all unit tests, then just run the "
+		println "	default 'build' task instead."
+		println ""
+
+		def char c
+
+		def console = System.console()
+		if (console) {
+			line = console.readLine('	> Do you want to continue? [y/N]: ')
+			line = line.trim()
+			c = line.charAt(0)
+		}
+		else {
+			println "	> Do you want to continue? [y/N]: "
+			println ""
+
+			// Can't use `console` because it does not work with Gradle Daemon
+			def DataInputStream dis = new DataInputStream(System.in);
+
+			c = dis.readByte()
+		}
+
+		if (!(c == 'Y' || c == 'y')) {
+			throw new StopExecutionException("Execution terminated by user")
+		}
+	}
+}
+
+task version() {
+	println "-----------------------"
+	println "  Jodd ${version}"
+	if (isSnapshot) println "  [snapshot]"
+	println "-----------------------"
+}
+
+// --- util -------------------------------------------------------------------
+
+def date() {
+	def date = new Date()
+	def formattedDate = date.format('yyyyMMdd')
+	return formattedDate
+}
+
+// --- wrapper ----------------------------------------------------------------
+
+task wrapper(type: Wrapper) {
+	gradleVersion = '3.5'
+}
diff --git a/buildSrc/src/main/groovy/CoverallsReporter.groovy b/.pc/01-disable-coverage-report.patch/buildSrc/src/main/groovy/CoverallsReporter.groovy
similarity index 100%
rename from buildSrc/src/main/groovy/CoverallsReporter.groovy
rename to .pc/01-disable-coverage-report.patch/buildSrc/src/main/groovy/CoverallsReporter.groovy
diff --git a/.pc/02-disable-bintray.patch/build.gradle b/.pc/02-disable-bintray.patch/build.gradle
new file mode 100644
index 0000000..abe488b
--- /dev/null
+++ b/.pc/02-disable-bintray.patch/build.gradle
@@ -0,0 +1,518 @@
+// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+plugins {
+	id "com.jfrog.bintray" version "1.6"
+}
+
+description = '''
+
+	Jodd is an open-source Java utility library and set of micro frameworks.
+	Jodd tools enriches JDK with many powerful and feature rich utilities.
+	It helps with everyday task, makes code more robust and reliable.
+	Jodd frameworks is set of lightweight application frameworks, compact yet powerful.
+	Designed following the CoC, DRY and SCS principles, it makes development
+	simple, but not simpler.
+'''
+
+//version = '3.8.6-SNAPSHOT'
+//version = '3.8.2-' + date()
+version = '3.8.6'
+
+// --- properties -------------------------------------------------------------
+
+ext {
+	gradleScriptDir = "${rootProject.projectDir}/gradle"
+
+	// libraries
+
+	tomcatVersion = '7.0.47'
+	jacocoVersion = '0.7.1.201405082137'
+
+	lib = [
+		mail:			[
+							'javax.mail:javax.mail-api:1.5.6',
+							'com.sun.mail:javax.mail:1.5.6'
+						],
+		activation:		'javax.activation:activation:1.1.1',
+		servlet:		'javax.servlet:javax.servlet-api:3.0.1',
+		jsp:			'javax.servlet.jsp:jsp-api:2.2',
+
+		log_slf4j:			'org.slf4j:slf4j-api:[1.7,1.8)',
+		log_slf4j_simple:	'org.slf4j:slf4j-simple:[1.7,1.8)',
+		log_logback_core: 	'ch.qos.logback:logback-core:[1.2,1.3)',
+		log_logback_classic: 'ch.qos.logback:logback-classic:[1.2,1.3)',
+		log_jcl:        	'commons-logging:commons-logging:[1.2,1.3)',
+		log_log4j2:		  'org.apache.logging.log4j:log4j-api:[2.8,2.9)',
+		log_log4j2_core:		  'org.apache.logging.log4j:log4j-core:[2.8,2.9)',
+
+//		asm:			'org.ow2.asm:asm:4.1',
+
+		junit: 			'junit:junit:4.12',
+		hamcrest:       'org.hamcrest:hamcrest-core:1.3',
+		mockito:		'org.mockito:mockito-all:1.10.19',
+		powermock:		[
+							'org.powermock:powermock-module-junit4:1.6.4',
+							'org.powermock:powermock-api-mockito:1.6.4'
+						],
+		hsqldb:			'org.hsqldb:hsqldb:2.2.9',
+		h2db:			'com.h2database:h2:1.1.111',
+		postgresql:		'org.postgresql:postgresql:9.4-1201-jdbc41',
+		mysql:			'mysql:mysql-connector-java:5.1.37',
+		winstone:		'net.sourceforge.winstone:winstone:0.9.10',
+		greenmail:		'com.icegreen:greenmail:1.5.2',
+		//el_api:		'javax.el:el-api:2.2',
+		el_api:			"org.apache.tomcat:tomcat-jasper:${tomcatVersion}",
+		tomcat_embed:	[
+							"org.apache.tomcat:tomcat-jasper:${tomcatVersion}",
+							"org.apache.tomcat:tomcat-jasper-el:${tomcatVersion}",
+							"org.apache.tomcat:tomcat-catalina:${tomcatVersion}",
+							"org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}",
+							"org.apache.tomcat.embed:tomcat-embed-jasper:${tomcatVersion}",
+							"org.apache.tomcat.embed:tomcat-embed-logging-juli:${tomcatVersion}",
+							"org.apache.tomcat.embed:tomcat-embed-logging-log4j:${tomcatVersion}"],
+		jacoco_agent:	"org.jacoco:org.jacoco.agent:${jacocoVersion}:runtime@jar",
+		jacoco_ant:		"org.jacoco:org.jacoco.ant:${jacocoVersion}",
+		mockserver:		'org.mock-server:mockserver-netty:3.10.4',
+	]
+
+	commonManifest = [
+		'Debug-Info' : 'on',
+		'Built-By' : 'jodd.org',
+		'Bundle-License' : 'http://jodd.org/license.html',
+		'Bundle-Vendor' : 'Jodd',
+		'Bundle-DocURL' : 'http://jodd.org',
+		'Implementation-Version': version,
+		'Implementation-Vendor': 'Jodd Team'
+	]
+}
+
+
+
+// --- modules sets -----------------------------------------------------------
+
+allprojects {
+	version = rootProject.version
+
+	ext.isSnapshot = version.contains("-");
+	ext.isSubproject = project.parent != null
+
+	repositories {
+		jcenter()
+		mavenCentral()
+	}
+}
+
+def javaModules() {
+	subprojects.findAll {
+		it.name.contains('jodd-') &&
+		!it.name.equals('jodd-bom') &&
+		!it.name.equals('jodd-all')
+	}
+}
+
+def javaBundleModules() {
+	rootProject.subprojects.findAll {
+		it.name.contains('jodd-') &&
+		!it.name.equals('jodd-swingspy') &&
+		!it.name.equals('jodd-joy') &&
+		!it.name.equals('jodd-bom') &&
+		!it.name.equals('jodd-all')
+	}
+}
+
+def javaMobileModules() {
+	rootProject.subprojects.findAll {
+		it.name.equals('jodd-core') ||
+		it.name.equals('jodd-bean') ||
+		it.name.equals('jodd-props')
+	}
+}
+
+
+// --- configuration ----------------------------------------------------------
+
+apply plugin: 'idea'
+apply plugin: 'eclipse'
+
+// exclude folders from IDEA project
+idea {
+	module {
+		excludeDirs += file('.idea')
+	}
+}
+
+configure(javaModules()) {
+	apply plugin: 'java'
+	apply plugin: 'osgi'
+	apply plugin: 'maven'
+	apply plugin: 'signing'
+	apply plugin: 'idea'
+	apply plugin: 'eclipse'
+	apply from: "${gradleScriptDir}/provided.gradle"
+	apply from: "${gradleScriptDir}/publish-maven.gradle"
+
+	group = 'org.jodd'
+
+	// bintray
+
+	apply plugin: 'com.jfrog.bintray'
+	apply from: "${gradleScriptDir}/bintray.gradle"
+
+	configurations {
+		published
+	}
+
+	// compile
+
+	compileJava {
+		sourceCompatibility = '1.8'
+		targetCompatibility = '1.8'
+	}
+
+	if (JavaVersion.current() != JavaVersion.VERSION_1_8) {
+		throw new GradleException("This build must be run with Java 8.")
+	}
+
+	tasks.withType(JavaCompile) {
+		options.encoding = 'UTF-8'
+		options.compilerArgs << "-Xlint:-options"
+		options.incremental = true
+		//configurations.compile.transitive = false
+	}
+	tasks.withType(Javadoc) {
+		options.addStringOption('Xdoclint:none', '-quiet')
+	}
+	compileJava.options.debug = true
+	compileJava.options.fork = true
+
+	task sourcesJar(type: Jar, dependsOn: classes) {
+		classifier = 'sources'
+		from sourceSets.main.allSource
+	}
+	task javadocJar(type: Jar, dependsOn: javadoc) {
+		classifier = 'javadoc'
+		from javadoc.destinationDir
+	}
+
+	// integration tests + performances
+
+	sourceSets {
+		perf {
+			java {
+				compileClasspath += main.output
+				runtimeClasspath += main.output
+			}
+		}
+		testInt {
+			java {
+				compileClasspath += main.output
+				compileClasspath += test.output
+				runtimeClasspath += main.output
+				runtimeClasspath += test.output
+			}
+		}
+	}
+
+	configurations {
+		testIntCompile.extendsFrom testCompile
+		testIntRuntime.extendsFrom testRuntime
+
+		perfCompile.extendsFrom testCompile
+		perfRuntime.extendsFrom testRuntime
+	}
+
+	dependencies {
+		perfCompile 'org.openjdk.jmh:jmh-core:1.15'
+		perfCompile 'org.openjdk.jmh:jmh-generator-annprocess:1.15'
+	}
+
+	idea {
+		module {
+			// DONT: use separate output folders for idea, don't mess with gradle
+			inheritOutputDirs false
+
+			//
+			// put our custom test dependencies onto IDEA's TEST scope
+			// IT IS NOT WORKING !!!
+			// see: https://youtrack.jetbrains.com/issue/IDEA-151925
+			// see: fixIdeaTestScopes.sh
+			//
+			//scopes.TEST.plus += [ configurations.testIntCompile ]
+			//scopes.TEST.plus += [ configurations.perfCompile ]
+		}
+	}
+
+	task perf(type:JavaExec) {
+		group 'Application'
+		description 'Execute benchmarks.'
+
+		main 'org.openjdk.jmh.Main'
+		classpath = sourceSets.perf.runtimeClasspath
+	}
+
+	task testIntegration(type: Test, dependsOn: jar) {
+		group 'Verification'
+		description 'Runs the integration tests.'
+		testLogging {
+			showStandardStreams = true
+		}
+
+		testClassesDir = sourceSets.testInt.output.classesDir
+		classpath = sourceSets.testInt.runtimeClasspath
+		systemProperties['jar.path'] = jar.archivePath
+	}
+
+	// artifacts
+
+	artifacts {
+		archives jar
+		archives sourcesJar
+		archives javadocJar
+
+		published jar
+		published sourcesJar
+		published javadocJar
+	}
+
+	jar {
+		afterEvaluate {
+			manifest.attributes << commonManifest
+			manifest {
+				name = project.moduleName
+				version = project.version
+				description = project.moduleDescription
+				attributes 'Implementation-Title': project.moduleName
+			}
+		}
+	}
+
+	javadoc {
+		exclude '**/asm5/**'
+		afterEvaluate {
+			configure(options) {
+				windowTitle 'Jodd API Documentation'
+				docTitle "$project.moduleName $project.version API Documentation"
+				bottom = 'Copyright &#169; 2003-2013 <a href="http://jodd.org">Jodd Team</a>'
+				breakIterator = true
+				author = false
+				encoding = 'UTF-8'
+				docEncoding = 'UTF-8'
+				stylesheetFile = file('src/main/javadoc/jodd.css')
+				source = '1.8'
+				failOnError = false
+			}
+		}
+	}
+
+	// Test: configure all tests, including integration
+
+	tasks.withType(Test) { testTask ->
+		configure(testTask) {
+			beforeTest { descriptor ->
+				logger.lifecycle("\t" + descriptor)
+			}
+
+			reports.html.enabled = true
+
+			systemProperties = System.properties
+
+			// we need to exclude certain classes as jacoco changes them and may influence testing results
+			List<String> excludes = ['*Test*', '*.?', '*Foo*', '*.data.*', '*.tst*', 'jodd.asm5.*', 'jodd.json.mo*', 'jodd.json.db*', '*.fixtures.*']
+
+			jvmArgs	'-Xms128m',
+			 	'-Xmx512m',
+			 	'-Duser.timezone=GMT'
+		}
+	}
+
+	// add fancy test output
+	tasks.withType(Test) {
+		afterSuite { desc, result ->
+			if (!desc.parent) { // will match the outermost suite
+				def output = "Results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} successes, ${result.failedTestCount} failures, ${result.skippedTestCount} skipped)"
+				def startItem = '|  ', endItem = '  |'
+				def repeatLength = startItem.length() + output.length() + endItem.length()
+				println('\n' + ('-' * repeatLength) + '\n' + startItem + output + endItem + '\n' + ('-' * repeatLength))
+			}
+		}
+	}
+
+}
+
+// --- project ----------------------------------------------------------------
+
+apply plugin: 'project-report'
+
+def javaBundleModulesSources = javaBundleModules().sourceSets.main
+
+task buildModules {
+	dependsOn javaModules().build
+}
+
+task testAll {
+	group 'Verification'
+	description 'Runs all the tests.'
+
+	//dependsOn javaTestModules().test
+
+	dependsOn javaModules().test
+	dependsOn javaModules().testIntegration
+}
+
+task clean(type: Delete) {
+	group 'Project'
+	description 'Cleans everything.'
+
+	delete buildDir
+}
+
+task javadocAll(type: Javadoc) {
+	group 'Documentation'
+	description 'Builds the aggregated Java docs for all modules.'
+	dependsOn buildModules
+
+	maxMemory '512m'
+
+	source javaBundleModulesSources.allJava
+	exclude '**/asm4/**'
+	exclude '**/asm5/**'
+
+	destinationDir = file("$buildDir/reports/javadoc")
+	classpath = files(javaBundleModulesSources.compileClasspath)
+
+	configure(options) {
+		windowTitle 'Jodd API Documentation'
+		docTitle "Jodd Project $project.version API Documentation"
+		bottom = 'Copyright &#169; 2003-2016 <a href="http://jodd.org">Jodd Team</a>'
+		breakIterator = true
+		author = false
+		encoding = 'UTF-8'
+		docEncoding = 'UTF-8'
+		stylesheetFile = file('src/main/javadoc/jodd.css')
+		source = '1.9'
+	}
+
+	inputs.files(javaBundleModulesSources.allSource + javaBundleModulesSources.compileClasspath)
+	outputs.dir destinationDir
+}
+
+task testReport(type: TestReport) {
+	group 'Reporting'
+	description 'Generates tests report.'
+
+	destinationDir = file("$buildDir/reports/tests")
+
+	//reportOn javaTestModules()*.test
+
+	reportOn javaBundleModules()*.test
+	reportOn javaBundleModules()*.testIntegration
+}
+
+dependencyReport {
+	projects = javaModules()
+}
+
+task bintray {
+	group 'Publishing'
+	description 'Publish all artifcats to bintray'
+
+	dependsOn javaModules().bintrayUpload
+	dependsOn(":jodd-bom:bintrayUpload")
+}
+
+// --- release ----------------------------------------------------------------
+
+task release() {
+	group 'Project'
+	description 'Builds everything for the release.'
+
+	dependsOn buildModules
+	dependsOn testAll
+	dependsOn javadocAll
+	dependsOn testReport
+	dependsOn ':distribution:build'
+	dependsOn projectReport
+}
+
+gradle.taskGraph.whenReady { taskGraph ->
+	if (taskGraph.hasTask(release)) {
+
+		println ""
+		println "	You are about to run the 'release' task for Jodd project!"
+		println "	This task builds distribution artifacts, but also runs"
+		println "	integration tests and generates reports (javadoc, coverage...)"
+		println "	For running integration tests you need infrastructure running;"
+		println "	please find more information here: http://jodd.org/code.html"
+		println ""
+		println "	Usually, you don't need to run this task. If you want to build"
+		println "	distribution jars and run all unit tests, then just run the "
+		println "	default 'build' task instead."
+		println ""
+
+		def char c
+
+		def console = System.console()
+		if (console) {
+			line = console.readLine('	> Do you want to continue? [y/N]: ')
+			line = line.trim()
+			c = line.charAt(0)
+		}
+		else {
+			println "	> Do you want to continue? [y/N]: "
+			println ""
+
+			// Can't use `console` because it does not work with Gradle Daemon
+			def DataInputStream dis = new DataInputStream(System.in);
+
+			c = dis.readByte()
+		}
+
+		if (!(c == 'Y' || c == 'y')) {
+			throw new StopExecutionException("Execution terminated by user")
+		}
+	}
+}
+
+task version() {
+	println "-----------------------"
+	println "  Jodd ${version}"
+	if (isSnapshot) println "  [snapshot]"
+	println "-----------------------"
+}
+
+// --- util -------------------------------------------------------------------
+
+def date() {
+	def date = new Date()
+	def formattedDate = date.format('yyyyMMdd')
+	return formattedDate
+}
+
+// --- wrapper ----------------------------------------------------------------
+
+task wrapper(type: Wrapper) {
+	gradleVersion = '3.5'
+}
diff --git a/.pc/02-disable-bintray.patch/jodd-all/build.gradle b/.pc/02-disable-bintray.patch/jodd-all/build.gradle
new file mode 100644
index 0000000..ce40db7
--- /dev/null
+++ b/.pc/02-disable-bintray.patch/jodd-all/build.gradle
@@ -0,0 +1,85 @@
+
+ext.moduleName = 'Jodd Bundle'
+ext.moduleDescription = 'Jodd bundle - all classes in one jar'
+
+apply plugin: 'java'
+apply plugin: 'maven'
+apply plugin: 'signing'
+apply from: "${gradleScriptDir}/provided.gradle"
+apply from: "${gradleScriptDir}/publish-maven.gradle"
+apply plugin: 'com.jfrog.bintray'
+apply from: "${gradleScriptDir}/bintray.gradle"
+
+group = 'org.jodd'
+
+dependencies {
+    compile javaBundleModules()
+}
+
+jar {
+    baseName = 'jodd-all'
+    javaBundleModules().each { subproject ->
+        from { subproject.sourceSets.main.output }
+    }
+}
+
+task sourcesJar(type: Jar, dependsOn: classes) {
+    classifier = 'sources'
+    from javaBundleModules().sourceSets.main.allSource
+}
+task javadocJar(type: Jar, dependsOn: javadoc) {
+    classifier = 'javadoc'
+    from javaBundleModules().javadoc.destinationDir
+}
+
+configurations {
+    published
+}
+
+artifacts {
+    archives jar
+    archives sourcesJar
+    archives javadocJar
+
+    published jar
+    published sourcesJar
+    published javadocJar
+}
+
+install {
+    repositories.mavenInstaller {
+        buildAllDependencies(pom)
+        addAllDependencies(pom)
+    }
+}
+
+def buildAllDependencies(pom) {
+    pom.whenConfigured {
+        p -> p.dependencies = p.dependencies.findAll {
+            dep -> dep.groupId != "org.jodd"
+        }
+    }
+}
+
+def addAllDependencies(pom) {
+    def deps = []
+    pom.withXml {
+        def dependenciesNode = asNode().appendNode('dependencies')
+
+        javaBundleModules().configurations.compile.allDependencies.each {
+            it.each {
+                if (it.group != "org.jodd") {
+                    def fulldep = it.group + ':' + it.name + ':' + it.version
+                    if (!(fulldep in deps)) {
+                        deps += [ fulldep ]
+                        def dependencyNode = dependenciesNode.appendNode('dependency')
+                        dependencyNode.appendNode('groupId', it.group)
+                        dependencyNode.appendNode('artifactId', it.name)
+                        dependencyNode.appendNode('version', it.version)
+                        dependencyNode.appendNode('optional', true)
+                    }
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/.pc/02-disable-bintray.patch/jodd-bom/build.gradle b/.pc/02-disable-bintray.patch/jodd-bom/build.gradle
new file mode 100644
index 0000000..ddd19e8
--- /dev/null
+++ b/.pc/02-disable-bintray.patch/jodd-bom/build.gradle
@@ -0,0 +1,56 @@
+
+ext.moduleName = 'jodd-bom'
+ext.moduleDescription = "Jodd (Bill of Materials)"
+
+apply plugin: 'java'    // needs to be java for GRADLE-2427
+apply plugin: 'maven'
+apply plugin: 'signing'
+apply from: "${gradleScriptDir}/publish-maven.gradle"
+apply plugin: 'com.jfrog.bintray'
+apply from: "${gradleScriptDir}/bintray.gradle"
+
+group = 'org.jodd'
+
+//configurations.archives.artifacts.clear()
+configurations {
+	published
+}
+
+artifacts {
+	// work around GRADLE-2406 by attaching text artifact
+	archives(file('jodd-bom.txt'))
+	published(file('jodd-bom.txt'))
+}
+
+install {
+	repositories.mavenInstaller {
+		buildBomDependencies(pom)
+	}
+}
+
+uploadArchives {
+	repositories.mavenDeployer {
+		buildBomDependencies(pom)
+	}
+}
+
+def buildBomDependencies(pom) {
+	pom.whenConfigured {
+		packaging = "pom"
+		withXml {
+			asNode().children().last() + {
+				delegate.dependencyManagement {
+					delegate.dependencies {
+						parent.javaModules().sort { "$it.name" }.each { p ->
+							delegate.dependency {
+								delegate.groupId(p.group)
+								delegate.artifactId(p.name)
+								delegate.version(p.version)
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+}
\ No newline at end of file
diff --git a/.pc/03-disable-java-check.patch/build.gradle b/.pc/03-disable-java-check.patch/build.gradle
new file mode 100644
index 0000000..c2a6178
--- /dev/null
+++ b/.pc/03-disable-java-check.patch/build.gradle
@@ -0,0 +1,506 @@
+// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+plugins {
+}
+
+description = '''
+
+	Jodd is an open-source Java utility library and set of micro frameworks.
+	Jodd tools enriches JDK with many powerful and feature rich utilities.
+	It helps with everyday task, makes code more robust and reliable.
+	Jodd frameworks is set of lightweight application frameworks, compact yet powerful.
+	Designed following the CoC, DRY and SCS principles, it makes development
+	simple, but not simpler.
+'''
+
+//version = '3.8.6-SNAPSHOT'
+//version = '3.8.2-' + date()
+version = '3.8.6'
+
+// --- properties -------------------------------------------------------------
+
+ext {
+	gradleScriptDir = "${rootProject.projectDir}/gradle"
+
+	// libraries
+
+	tomcatVersion = '7.0.47'
+	jacocoVersion = '0.7.1.201405082137'
+
+	lib = [
+		mail:			[
+							'javax.mail:javax.mail-api:1.5.6',
+							'com.sun.mail:javax.mail:1.5.6'
+						],
+		activation:		'javax.activation:activation:1.1.1',
+		servlet:		'javax.servlet:javax.servlet-api:3.0.1',
+		jsp:			'javax.servlet.jsp:jsp-api:2.2',
+
+		log_slf4j:			'org.slf4j:slf4j-api:[1.7,1.8)',
+		log_slf4j_simple:	'org.slf4j:slf4j-simple:[1.7,1.8)',
+		log_logback_core: 	'ch.qos.logback:logback-core:[1.2,1.3)',
+		log_logback_classic: 'ch.qos.logback:logback-classic:[1.2,1.3)',
+		log_jcl:        	'commons-logging:commons-logging:[1.2,1.3)',
+		log_log4j2:		  'org.apache.logging.log4j:log4j-api:[2.8,2.9)',
+		log_log4j2_core:		  'org.apache.logging.log4j:log4j-core:[2.8,2.9)',
+
+//		asm:			'org.ow2.asm:asm:4.1',
+
+		junit: 			'junit:junit:4.12',
+		hamcrest:       'org.hamcrest:hamcrest-core:1.3',
+		mockito:		'org.mockito:mockito-all:1.10.19',
+		powermock:		[
+							'org.powermock:powermock-module-junit4:1.6.4',
+							'org.powermock:powermock-api-mockito:1.6.4'
+						],
+		hsqldb:			'org.hsqldb:hsqldb:2.2.9',
+		h2db:			'com.h2database:h2:1.1.111',
+		postgresql:		'org.postgresql:postgresql:9.4-1201-jdbc41',
+		mysql:			'mysql:mysql-connector-java:5.1.37',
+		winstone:		'net.sourceforge.winstone:winstone:0.9.10',
+		greenmail:		'com.icegreen:greenmail:1.5.2',
+		//el_api:		'javax.el:el-api:2.2',
+		el_api:			"org.apache.tomcat:tomcat-jasper:${tomcatVersion}",
+		tomcat_embed:	[
+							"org.apache.tomcat:tomcat-jasper:${tomcatVersion}",
+							"org.apache.tomcat:tomcat-jasper-el:${tomcatVersion}",
+							"org.apache.tomcat:tomcat-catalina:${tomcatVersion}",
+							"org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}",
+							"org.apache.tomcat.embed:tomcat-embed-jasper:${tomcatVersion}",
+							"org.apache.tomcat.embed:tomcat-embed-logging-juli:${tomcatVersion}",
+							"org.apache.tomcat.embed:tomcat-embed-logging-log4j:${tomcatVersion}"],
+		jacoco_agent:	"org.jacoco:org.jacoco.agent:${jacocoVersion}:runtime@jar",
+		jacoco_ant:		"org.jacoco:org.jacoco.ant:${jacocoVersion}",
+		mockserver:		'org.mock-server:mockserver-netty:3.10.4',
+	]
+
+	commonManifest = [
+		'Debug-Info' : 'on',
+		'Built-By' : 'jodd.org',
+		'Bundle-License' : 'http://jodd.org/license.html',
+		'Bundle-Vendor' : 'Jodd',
+		'Bundle-DocURL' : 'http://jodd.org',
+		'Implementation-Version': version,
+		'Implementation-Vendor': 'Jodd Team'
+	]
+}
+
+
+
+// --- modules sets -----------------------------------------------------------
+
+allprojects {
+	version = rootProject.version
+
+	ext.isSnapshot = version.contains("-");
+	ext.isSubproject = project.parent != null
+
+	repositories {
+		jcenter()
+		mavenCentral()
+	}
+}
+
+def javaModules() {
+	subprojects.findAll {
+		it.name.contains('jodd-') &&
+		!it.name.equals('jodd-bom') &&
+		!it.name.equals('jodd-all')
+	}
+}
+
+def javaBundleModules() {
+	rootProject.subprojects.findAll {
+		it.name.contains('jodd-') &&
+		!it.name.equals('jodd-swingspy') &&
+		!it.name.equals('jodd-joy') &&
+		!it.name.equals('jodd-bom') &&
+		!it.name.equals('jodd-all')
+	}
+}
+
+def javaMobileModules() {
+	rootProject.subprojects.findAll {
+		it.name.equals('jodd-core') ||
+		it.name.equals('jodd-bean') ||
+		it.name.equals('jodd-props')
+	}
+}
+
+
+// --- configuration ----------------------------------------------------------
+
+apply plugin: 'idea'
+apply plugin: 'eclipse'
+
+// exclude folders from IDEA project
+idea {
+	module {
+		excludeDirs += file('.idea')
+	}
+}
+
+configure(javaModules()) {
+	apply plugin: 'java'
+	apply plugin: 'osgi'
+	apply plugin: 'maven'
+	apply plugin: 'signing'
+	apply plugin: 'idea'
+	apply plugin: 'eclipse'
+	apply from: "${gradleScriptDir}/provided.gradle"
+	apply from: "${gradleScriptDir}/publish-maven.gradle"
+
+	group = 'org.jodd'
+
+	// bintray
+
+	configurations {
+		published
+	}
+
+	// compile
+
+	compileJava {
+		sourceCompatibility = '1.8'
+		targetCompatibility = '1.8'
+	}
+
+	if (JavaVersion.current() != JavaVersion.VERSION_1_8) {
+		throw new GradleException("This build must be run with Java 8.")
+	}
+
+	tasks.withType(JavaCompile) {
+		options.encoding = 'UTF-8'
+		options.compilerArgs << "-Xlint:-options"
+		options.incremental = true
+		//configurations.compile.transitive = false
+	}
+	tasks.withType(Javadoc) {
+		options.addStringOption('Xdoclint:none', '-quiet')
+	}
+	compileJava.options.debug = true
+	compileJava.options.fork = true
+
+	task sourcesJar(type: Jar, dependsOn: classes) {
+		classifier = 'sources'
+		from sourceSets.main.allSource
+	}
+	task javadocJar(type: Jar, dependsOn: javadoc) {
+		classifier = 'javadoc'
+		from javadoc.destinationDir
+	}
+
+	// integration tests + performances
+
+	sourceSets {
+		perf {
+			java {
+				compileClasspath += main.output
+				runtimeClasspath += main.output
+			}
+		}
+		testInt {
+			java {
+				compileClasspath += main.output
+				compileClasspath += test.output
+				runtimeClasspath += main.output
+				runtimeClasspath += test.output
+			}
+		}
+	}
+
+	configurations {
+		testIntCompile.extendsFrom testCompile
+		testIntRuntime.extendsFrom testRuntime
+
+		perfCompile.extendsFrom testCompile
+		perfRuntime.extendsFrom testRuntime
+	}
+
+	dependencies {
+		perfCompile 'org.openjdk.jmh:jmh-core:1.15'
+		perfCompile 'org.openjdk.jmh:jmh-generator-annprocess:1.15'
+	}
+
+	idea {
+		module {
+			// DONT: use separate output folders for idea, don't mess with gradle
+			inheritOutputDirs false
+
+			//
+			// put our custom test dependencies onto IDEA's TEST scope
+			// IT IS NOT WORKING !!!
+			// see: https://youtrack.jetbrains.com/issue/IDEA-151925
+			// see: fixIdeaTestScopes.sh
+			//
+			//scopes.TEST.plus += [ configurations.testIntCompile ]
+			//scopes.TEST.plus += [ configurations.perfCompile ]
+		}
+	}
+
+	task perf(type:JavaExec) {
+		group 'Application'
+		description 'Execute benchmarks.'
+
+		main 'org.openjdk.jmh.Main'
+		classpath = sourceSets.perf.runtimeClasspath
+	}
+
+	task testIntegration(type: Test, dependsOn: jar) {
+		group 'Verification'
+		description 'Runs the integration tests.'
+		testLogging {
+			showStandardStreams = true
+		}
+
+		testClassesDir = sourceSets.testInt.output.classesDir
+		classpath = sourceSets.testInt.runtimeClasspath
+		systemProperties['jar.path'] = jar.archivePath
+	}
+
+	// artifacts
+
+	artifacts {
+		archives jar
+		archives sourcesJar
+		archives javadocJar
+
+		published jar
+		published sourcesJar
+		published javadocJar
+	}
+
+	jar {
+		afterEvaluate {
+			manifest.attributes << commonManifest
+			manifest {
+				name = project.moduleName
+				version = project.version
+				description = project.moduleDescription
+				attributes 'Implementation-Title': project.moduleName
+			}
+		}
+	}
+
+	javadoc {
+		exclude '**/asm5/**'
+		afterEvaluate {
+			configure(options) {
+				windowTitle 'Jodd API Documentation'
+				docTitle "$project.moduleName $project.version API Documentation"
+				bottom = 'Copyright &#169; 2003-2013 <a href="http://jodd.org">Jodd Team</a>'
+				breakIterator = true
+				author = false
+				encoding = 'UTF-8'
+				docEncoding = 'UTF-8'
+				stylesheetFile = file('src/main/javadoc/jodd.css')
+				source = '1.8'
+				failOnError = false
+			}
+		}
+	}
+
+	// Test: configure all tests, including integration
+
+	tasks.withType(Test) { testTask ->
+		configure(testTask) {
+			beforeTest { descriptor ->
+				logger.lifecycle("\t" + descriptor)
+			}
+
+			reports.html.enabled = true
+
+			systemProperties = System.properties
+
+			// we need to exclude certain classes as jacoco changes them and may influence testing results
+			List<String> excludes = ['*Test*', '*.?', '*Foo*', '*.data.*', '*.tst*', 'jodd.asm5.*', 'jodd.json.mo*', 'jodd.json.db*', '*.fixtures.*']
+
+			jvmArgs	'-Xms128m',
+			 	'-Xmx512m',
+			 	'-Duser.timezone=GMT'
+		}
+	}
+
+	// add fancy test output
+	tasks.withType(Test) {
+		afterSuite { desc, result ->
+			if (!desc.parent) { // will match the outermost suite
+				def output = "Results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} successes, ${result.failedTestCount} failures, ${result.skippedTestCount} skipped)"
+				def startItem = '|  ', endItem = '  |'
+				def repeatLength = startItem.length() + output.length() + endItem.length()
+				println('\n' + ('-' * repeatLength) + '\n' + startItem + output + endItem + '\n' + ('-' * repeatLength))
+			}
+		}
+	}
+
+}
+
+// --- project ----------------------------------------------------------------
+
+apply plugin: 'project-report'
+
+def javaBundleModulesSources = javaBundleModules().sourceSets.main
+
+task buildModules {
+	dependsOn javaModules().build
+}
+
+task testAll {
+	group 'Verification'
+	description 'Runs all the tests.'
+
+	//dependsOn javaTestModules().test
+
+	dependsOn javaModules().test
+	dependsOn javaModules().testIntegration
+}
+
+task clean(type: Delete) {
+	group 'Project'
+	description 'Cleans everything.'
+
+	delete buildDir
+}
+
+task javadocAll(type: Javadoc) {
+	group 'Documentation'
+	description 'Builds the aggregated Java docs for all modules.'
+	dependsOn buildModules
+
+	maxMemory '512m'
+
+	source javaBundleModulesSources.allJava
+	exclude '**/asm4/**'
+	exclude '**/asm5/**'
+
+	destinationDir = file("$buildDir/reports/javadoc")
+	classpath = files(javaBundleModulesSources.compileClasspath)
+
+	configure(options) {
+		windowTitle 'Jodd API Documentation'
+		docTitle "Jodd Project $project.version API Documentation"
+		bottom = 'Copyright &#169; 2003-2016 <a href="http://jodd.org">Jodd Team</a>'
+		breakIterator = true
+		author = false
+		encoding = 'UTF-8'
+		docEncoding = 'UTF-8'
+		stylesheetFile = file('src/main/javadoc/jodd.css')
+		source = '1.9'
+	}
+
+	inputs.files(javaBundleModulesSources.allSource + javaBundleModulesSources.compileClasspath)
+	outputs.dir destinationDir
+}
+
+task testReport(type: TestReport) {
+	group 'Reporting'
+	description 'Generates tests report.'
+
+	destinationDir = file("$buildDir/reports/tests")
+
+	//reportOn javaTestModules()*.test
+
+	reportOn javaBundleModules()*.test
+	reportOn javaBundleModules()*.testIntegration
+}
+
+dependencyReport {
+	projects = javaModules()
+}
+
+// --- release ----------------------------------------------------------------
+
+task release() {
+	group 'Project'
+	description 'Builds everything for the release.'
+
+	dependsOn buildModules
+	dependsOn testAll
+	dependsOn javadocAll
+	dependsOn testReport
+	dependsOn ':distribution:build'
+	dependsOn projectReport
+}
+
+gradle.taskGraph.whenReady { taskGraph ->
+	if (taskGraph.hasTask(release)) {
+
+		println ""
+		println "	You are about to run the 'release' task for Jodd project!"
+		println "	This task builds distribution artifacts, but also runs"
+		println "	integration tests and generates reports (javadoc, coverage...)"
+		println "	For running integration tests you need infrastructure running;"
+		println "	please find more information here: http://jodd.org/code.html"
+		println ""
+		println "	Usually, you don't need to run this task. If you want to build"
+		println "	distribution jars and run all unit tests, then just run the "
+		println "	default 'build' task instead."
+		println ""
+
+		def char c
+
+		def console = System.console()
+		if (console) {
+			line = console.readLine('	> Do you want to continue? [y/N]: ')
+			line = line.trim()
+			c = line.charAt(0)
+		}
+		else {
+			println "	> Do you want to continue? [y/N]: "
+			println ""
+
+			// Can't use `console` because it does not work with Gradle Daemon
+			def DataInputStream dis = new DataInputStream(System.in);
+
+			c = dis.readByte()
+		}
+
+		if (!(c == 'Y' || c == 'y')) {
+			throw new StopExecutionException("Execution terminated by user")
+		}
+	}
+}
+
+task version() {
+	println "-----------------------"
+	println "  Jodd ${version}"
+	if (isSnapshot) println "  [snapshot]"
+	println "-----------------------"
+}
+
+// --- util -------------------------------------------------------------------
+
+def date() {
+	def date = new Date()
+	def formattedDate = date.format('yyyyMMdd')
+	return formattedDate
+}
+
+// --- wrapper ----------------------------------------------------------------
+
+task wrapper(type: Wrapper) {
+	gradleVersion = '3.5'
+}
diff --git a/.pc/04-disable-modules.patch/settings.gradle b/.pc/04-disable-modules.patch/settings.gradle
new file mode 100644
index 0000000..c0f5b15
--- /dev/null
+++ b/.pc/04-disable-modules.patch/settings.gradle
@@ -0,0 +1,25 @@
+include 'distribution'
+
+include 'jodd-all'
+include 'jodd-bean'
+include 'jodd-core'
+include 'jodd-db'
+include 'jodd-decora'
+include 'jodd-htmlstapler'
+include 'jodd-http'
+include 'jodd-joy'
+include 'jodd-json'
+include 'jodd-jtx'
+include 'jodd-lagarto'
+include 'jodd-log'
+include 'jodd-madvoc'
+include 'jodd-mail'
+include 'jodd-petite'
+include 'jodd-props'
+include 'jodd-proxetta'
+include 'jodd-servlet'
+include 'jodd-swingspy'
+include 'jodd-upload'
+include 'jodd-vtor'
+
+include 'jodd-bom'
\ No newline at end of file
diff --git a/.pc/05-remove-version-ranges.patch/build.gradle b/.pc/05-remove-version-ranges.patch/build.gradle
new file mode 100644
index 0000000..e3dbf21
--- /dev/null
+++ b/.pc/05-remove-version-ranges.patch/build.gradle
@@ -0,0 +1,502 @@
+// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+plugins {
+}
+
+description = '''
+
+	Jodd is an open-source Java utility library and set of micro frameworks.
+	Jodd tools enriches JDK with many powerful and feature rich utilities.
+	It helps with everyday task, makes code more robust and reliable.
+	Jodd frameworks is set of lightweight application frameworks, compact yet powerful.
+	Designed following the CoC, DRY and SCS principles, it makes development
+	simple, but not simpler.
+'''
+
+//version = '3.8.6-SNAPSHOT'
+//version = '3.8.2-' + date()
+version = '3.8.6'
+
+// --- properties -------------------------------------------------------------
+
+ext {
+	gradleScriptDir = "${rootProject.projectDir}/gradle"
+
+	// libraries
+
+	tomcatVersion = '7.0.47'
+	jacocoVersion = '0.7.1.201405082137'
+
+	lib = [
+		mail:			[
+							'javax.mail:javax.mail-api:1.5.6',
+							'com.sun.mail:javax.mail:1.5.6'
+						],
+		activation:		'javax.activation:activation:1.1.1',
+		servlet:		'javax.servlet:javax.servlet-api:3.0.1',
+		jsp:			'javax.servlet.jsp:jsp-api:2.2',
+
+		log_slf4j:			'org.slf4j:slf4j-api:[1.7,1.8)',
+		log_slf4j_simple:	'org.slf4j:slf4j-simple:[1.7,1.8)',
+		log_logback_core: 	'ch.qos.logback:logback-core:[1.2,1.3)',
+		log_logback_classic: 'ch.qos.logback:logback-classic:[1.2,1.3)',
+		log_jcl:        	'commons-logging:commons-logging:[1.2,1.3)',
+		log_log4j2:		  'org.apache.logging.log4j:log4j-api:[2.8,2.9)',
+		log_log4j2_core:		  'org.apache.logging.log4j:log4j-core:[2.8,2.9)',
+
+//		asm:			'org.ow2.asm:asm:4.1',
+
+		junit: 			'junit:junit:4.12',
+		hamcrest:       'org.hamcrest:hamcrest-core:1.3',
+		mockito:		'org.mockito:mockito-all:1.10.19',
+		powermock:		[
+							'org.powermock:powermock-module-junit4:1.6.4',
+							'org.powermock:powermock-api-mockito:1.6.4'
+						],
+		hsqldb:			'org.hsqldb:hsqldb:2.2.9',
+		h2db:			'com.h2database:h2:1.1.111',
+		postgresql:		'org.postgresql:postgresql:9.4-1201-jdbc41',
+		mysql:			'mysql:mysql-connector-java:5.1.37',
+		winstone:		'net.sourceforge.winstone:winstone:0.9.10',
+		greenmail:		'com.icegreen:greenmail:1.5.2',
+		//el_api:		'javax.el:el-api:2.2',
+		el_api:			"org.apache.tomcat:tomcat-jasper:${tomcatVersion}",
+		tomcat_embed:	[
+							"org.apache.tomcat:tomcat-jasper:${tomcatVersion}",
+							"org.apache.tomcat:tomcat-jasper-el:${tomcatVersion}",
+							"org.apache.tomcat:tomcat-catalina:${tomcatVersion}",
+							"org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}",
+							"org.apache.tomcat.embed:tomcat-embed-jasper:${tomcatVersion}",
+							"org.apache.tomcat.embed:tomcat-embed-logging-juli:${tomcatVersion}",
+							"org.apache.tomcat.embed:tomcat-embed-logging-log4j:${tomcatVersion}"],
+		jacoco_agent:	"org.jacoco:org.jacoco.agent:${jacocoVersion}:runtime@jar",
+		jacoco_ant:		"org.jacoco:org.jacoco.ant:${jacocoVersion}",
+		mockserver:		'org.mock-server:mockserver-netty:3.10.4',
+	]
+
+	commonManifest = [
+		'Debug-Info' : 'on',
+		'Built-By' : 'jodd.org',
+		'Bundle-License' : 'http://jodd.org/license.html',
+		'Bundle-Vendor' : 'Jodd',
+		'Bundle-DocURL' : 'http://jodd.org',
+		'Implementation-Version': version,
+		'Implementation-Vendor': 'Jodd Team'
+	]
+}
+
+
+
+// --- modules sets -----------------------------------------------------------
+
+allprojects {
+	version = rootProject.version
+
+	ext.isSnapshot = version.contains("-");
+	ext.isSubproject = project.parent != null
+
+	repositories {
+		jcenter()
+		mavenCentral()
+	}
+}
+
+def javaModules() {
+	subprojects.findAll {
+		it.name.contains('jodd-') &&
+		!it.name.equals('jodd-bom') &&
+		!it.name.equals('jodd-all')
+	}
+}
+
+def javaBundleModules() {
+	rootProject.subprojects.findAll {
+		it.name.contains('jodd-') &&
+		!it.name.equals('jodd-swingspy') &&
+		!it.name.equals('jodd-joy') &&
+		!it.name.equals('jodd-bom') &&
+		!it.name.equals('jodd-all')
+	}
+}
+
+def javaMobileModules() {
+	rootProject.subprojects.findAll {
+		it.name.equals('jodd-core') ||
+		it.name.equals('jodd-bean') ||
+		it.name.equals('jodd-props')
+	}
+}
+
+
+// --- configuration ----------------------------------------------------------
+
+apply plugin: 'idea'
+apply plugin: 'eclipse'
+
+// exclude folders from IDEA project
+idea {
+	module {
+		excludeDirs += file('.idea')
+	}
+}
+
+configure(javaModules()) {
+	apply plugin: 'java'
+	apply plugin: 'osgi'
+	apply plugin: 'maven'
+	apply plugin: 'signing'
+	apply plugin: 'idea'
+	apply plugin: 'eclipse'
+	apply from: "${gradleScriptDir}/provided.gradle"
+	apply from: "${gradleScriptDir}/publish-maven.gradle"
+
+	group = 'org.jodd'
+
+	// bintray
+
+	configurations {
+		published
+	}
+
+	// compile
+
+	compileJava {
+		sourceCompatibility = '1.8'
+		targetCompatibility = '1.8'
+	}
+
+	tasks.withType(JavaCompile) {
+		options.encoding = 'UTF-8'
+		options.compilerArgs << "-Xlint:-options"
+		options.incremental = true
+		//configurations.compile.transitive = false
+	}
+	tasks.withType(Javadoc) {
+		options.addStringOption('Xdoclint:none', '-quiet')
+	}
+	compileJava.options.debug = true
+	compileJava.options.fork = true
+
+	task sourcesJar(type: Jar, dependsOn: classes) {
+		classifier = 'sources'
+		from sourceSets.main.allSource
+	}
+	task javadocJar(type: Jar, dependsOn: javadoc) {
+		classifier = 'javadoc'
+		from javadoc.destinationDir
+	}
+
+	// integration tests + performances
+
+	sourceSets {
+		perf {
+			java {
+				compileClasspath += main.output
+				runtimeClasspath += main.output
+			}
+		}
+		testInt {
+			java {
+				compileClasspath += main.output
+				compileClasspath += test.output
+				runtimeClasspath += main.output
+				runtimeClasspath += test.output
+			}
+		}
+	}
+
+	configurations {
+		testIntCompile.extendsFrom testCompile
+		testIntRuntime.extendsFrom testRuntime
+
+		perfCompile.extendsFrom testCompile
+		perfRuntime.extendsFrom testRuntime
+	}
+
+	dependencies {
+		perfCompile 'org.openjdk.jmh:jmh-core:1.15'
+		perfCompile 'org.openjdk.jmh:jmh-generator-annprocess:1.15'
+	}
+
+	idea {
+		module {
+			// DONT: use separate output folders for idea, don't mess with gradle
+			inheritOutputDirs false
+
+			//
+			// put our custom test dependencies onto IDEA's TEST scope
+			// IT IS NOT WORKING !!!
+			// see: https://youtrack.jetbrains.com/issue/IDEA-151925
+			// see: fixIdeaTestScopes.sh
+			//
+			//scopes.TEST.plus += [ configurations.testIntCompile ]
+			//scopes.TEST.plus += [ configurations.perfCompile ]
+		}
+	}
+
+	task perf(type:JavaExec) {
+		group 'Application'
+		description 'Execute benchmarks.'
+
+		main 'org.openjdk.jmh.Main'
+		classpath = sourceSets.perf.runtimeClasspath
+	}
+
+	task testIntegration(type: Test, dependsOn: jar) {
+		group 'Verification'
+		description 'Runs the integration tests.'
+		testLogging {
+			showStandardStreams = true
+		}
+
+		testClassesDir = sourceSets.testInt.output.classesDir
+		classpath = sourceSets.testInt.runtimeClasspath
+		systemProperties['jar.path'] = jar.archivePath
+	}
+
+	// artifacts
+
+	artifacts {
+		archives jar
+		archives sourcesJar
+		archives javadocJar
+
+		published jar
+		published sourcesJar
+		published javadocJar
+	}
+
+	jar {
+		afterEvaluate {
+			manifest.attributes << commonManifest
+			manifest {
+				name = project.moduleName
+				version = project.version
+				description = project.moduleDescription
+				attributes 'Implementation-Title': project.moduleName
+			}
+		}
+	}
+
+	javadoc {
+		exclude '**/asm5/**'
+		afterEvaluate {
+			configure(options) {
+				windowTitle 'Jodd API Documentation'
+				docTitle "$project.moduleName $project.version API Documentation"
+				bottom = 'Copyright &#169; 2003-2013 <a href="http://jodd.org">Jodd Team</a>'
+				breakIterator = true
+				author = false
+				encoding = 'UTF-8'
+				docEncoding = 'UTF-8'
+				stylesheetFile = file('src/main/javadoc/jodd.css')
+				source = '1.8'
+				failOnError = false
+			}
+		}
+	}
+
+	// Test: configure all tests, including integration
+
+	tasks.withType(Test) { testTask ->
+		configure(testTask) {
+			beforeTest { descriptor ->
+				logger.lifecycle("\t" + descriptor)
+			}
+
+			reports.html.enabled = true
+
+			systemProperties = System.properties
+
+			// we need to exclude certain classes as jacoco changes them and may influence testing results
+			List<String> excludes = ['*Test*', '*.?', '*Foo*', '*.data.*', '*.tst*', 'jodd.asm5.*', 'jodd.json.mo*', 'jodd.json.db*', '*.fixtures.*']
+
+			jvmArgs	'-Xms128m',
+			 	'-Xmx512m',
+			 	'-Duser.timezone=GMT'
+		}
+	}
+
+	// add fancy test output
+	tasks.withType(Test) {
+		afterSuite { desc, result ->
+			if (!desc.parent) { // will match the outermost suite
+				def output = "Results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} successes, ${result.failedTestCount} failures, ${result.skippedTestCount} skipped)"
+				def startItem = '|  ', endItem = '  |'
+				def repeatLength = startItem.length() + output.length() + endItem.length()
+				println('\n' + ('-' * repeatLength) + '\n' + startItem + output + endItem + '\n' + ('-' * repeatLength))
+			}
+		}
+	}
+
+}
+
+// --- project ----------------------------------------------------------------
+
+apply plugin: 'project-report'
+
+def javaBundleModulesSources = javaBundleModules().sourceSets.main
+
+task buildModules {
+	dependsOn javaModules().build
+}
+
+task testAll {
+	group 'Verification'
+	description 'Runs all the tests.'
+
+	//dependsOn javaTestModules().test
+
+	dependsOn javaModules().test
+	dependsOn javaModules().testIntegration
+}
+
+task clean(type: Delete) {
+	group 'Project'
+	description 'Cleans everything.'
+
+	delete buildDir
+}
+
+task javadocAll(type: Javadoc) {
+	group 'Documentation'
+	description 'Builds the aggregated Java docs for all modules.'
+	dependsOn buildModules
+
+	maxMemory '512m'
+
+	source javaBundleModulesSources.allJava
+	exclude '**/asm4/**'
+	exclude '**/asm5/**'
+
+	destinationDir = file("$buildDir/reports/javadoc")
+	classpath = files(javaBundleModulesSources.compileClasspath)
+
+	configure(options) {
+		windowTitle 'Jodd API Documentation'
+		docTitle "Jodd Project $project.version API Documentation"
+		bottom = 'Copyright &#169; 2003-2016 <a href="http://jodd.org">Jodd Team</a>'
+		breakIterator = true
+		author = false
+		encoding = 'UTF-8'
+		docEncoding = 'UTF-8'
+		stylesheetFile = file('src/main/javadoc/jodd.css')
+		source = '1.9'
+	}
+
+	inputs.files(javaBundleModulesSources.allSource + javaBundleModulesSources.compileClasspath)
+	outputs.dir destinationDir
+}
+
+task testReport(type: TestReport) {
+	group 'Reporting'
+	description 'Generates tests report.'
+
+	destinationDir = file("$buildDir/reports/tests")
+
+	//reportOn javaTestModules()*.test
+
+	reportOn javaBundleModules()*.test
+	reportOn javaBundleModules()*.testIntegration
+}
+
+dependencyReport {
+	projects = javaModules()
+}
+
+// --- release ----------------------------------------------------------------
+
+task release() {
+	group 'Project'
+	description 'Builds everything for the release.'
+
+	dependsOn buildModules
+	dependsOn testAll
+	dependsOn javadocAll
+	dependsOn testReport
+	dependsOn ':distribution:build'
+	dependsOn projectReport
+}
+
+gradle.taskGraph.whenReady { taskGraph ->
+	if (taskGraph.hasTask(release)) {
+
+		println ""
+		println "	You are about to run the 'release' task for Jodd project!"
+		println "	This task builds distribution artifacts, but also runs"
+		println "	integration tests and generates reports (javadoc, coverage...)"
+		println "	For running integration tests you need infrastructure running;"
+		println "	please find more information here: http://jodd.org/code.html"
+		println ""
+		println "	Usually, you don't need to run this task. If you want to build"
+		println "	distribution jars and run all unit tests, then just run the "
+		println "	default 'build' task instead."
+		println ""
+
+		def char c
+
+		def console = System.console()
+		if (console) {
+			line = console.readLine('	> Do you want to continue? [y/N]: ')
+			line = line.trim()
+			c = line.charAt(0)
+		}
+		else {
+			println "	> Do you want to continue? [y/N]: "
+			println ""
+
+			// Can't use `console` because it does not work with Gradle Daemon
+			def DataInputStream dis = new DataInputStream(System.in);
+
+			c = dis.readByte()
+		}
+
+		if (!(c == 'Y' || c == 'y')) {
+			throw new StopExecutionException("Execution terminated by user")
+		}
+	}
+}
+
+task version() {
+	println "-----------------------"
+	println "  Jodd ${version}"
+	if (isSnapshot) println "  [snapshot]"
+	println "-----------------------"
+}
+
+// --- util -------------------------------------------------------------------
+
+def date() {
+	def date = new Date()
+	def formattedDate = date.format('yyyyMMdd')
+	return formattedDate
+}
+
+// --- wrapper ----------------------------------------------------------------
+
+task wrapper(type: Wrapper) {
+	gradleVersion = '3.5'
+}
diff --git a/.pc/06-remove-generated-annotation.patch/jodd-core/src/main/java/jodd/util/ArraysUtil.java b/.pc/06-remove-generated-annotation.patch/jodd-core/src/main/java/jodd/util/ArraysUtil.java
new file mode 100644
index 0000000..cb98090
--- /dev/null
+++ b/.pc/06-remove-generated-annotation.patch/jodd-core/src/main/java/jodd/util/ArraysUtil.java
@@ -0,0 +1,2406 @@
+// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+package jodd.util;
+
+import javax.annotation.Generated;
+import java.lang.reflect.Array;
+import static jodd.util.StringPool.NULL;
+
+/**
+ * Array utilities.
+ */
+@Generated("ArraysUtil.py")
+public class ArraysUtil {
+
+
+	// ---------------------------------------------------------------- wrap
+
+	/**
+	 * Wraps elements into an array.
+	 */
+	public static <T> T[] array(T... elements) {
+		return elements;
+	}
+
+	/**
+	 * Wraps elements into an array.
+	 */
+	public static byte[] bytes(byte... elements) {
+		return elements;
+	}
+
+	/**
+	 * Wraps elements into an array.
+	 */
+	public static char[] chars(char... elements) {
+		return elements;
+	}
+
+	/**
+	 * Wraps elements into an array.
+	 */
+	public static short[] shorts(short... elements) {
+		return elements;
+	}
+
+	/**
+	 * Wraps elements into an array.
+	 */
+	public static int[] ints(int... elements) {
+		return elements;
+	}
+
+	/**
+	 * Wraps elements into an array.
+	 */
+	public static long[] longs(long... elements) {
+		return elements;
+	}
+
+	/**
+	 * Wraps elements into an array.
+	 */
+	public static float[] floats(float... elements) {
+		return elements;
+	}
+
+	/**
+	 * Wraps elements into an array.
+	 */
+	public static double[] doubles(double... elements) {
+		return elements;
+	}
+
+	/**
+	 * Wraps elements into an array.
+	 */
+	public static boolean[] booleans(boolean... elements) {
+		return elements;
+	}
+
+
+	// ---------------------------------------------------------------- join
+
+	/**
+	 * Joins arrays. Component type is resolved from the array argument.
+	 */
+	@SuppressWarnings({"unchecked"})
+	public static <T> T[] join(T[]... arrays) {
+		Class<T> componentType = (Class<T>) arrays.getClass().getComponentType().getComponentType();
+		return join(componentType, arrays);
+	}
+
+	/**
+	 * Joins arrays using provided component type.
+	 */
+	@SuppressWarnings({"unchecked"})
+	public static <T> T[] join(Class<T> componentType, T[][] arrays) {
+		if (arrays.length == 1) {
+			return arrays[0];
+		}
+		int length = 0;
+		for (T[] array : arrays) {
+			length += array.length;
+		}
+		T[] result = (T[]) Array.newInstance(componentType, length);
+
+		length = 0;
+		for (T[] array : arrays) {
+			System.arraycopy(array, 0, result, length, array.length);
+			length += array.length;
+		}
+		return result;
+	}
+
+
+	/**
+	 * Join <code>String</code> arrays.
+	 */
+	public static String[] join(String[]... arrays) {
+		if (arrays.length == 0) {
+			return new String[0];
+		}
+		if (arrays.length == 1) {
+			return arrays[0];
+		}
+		int length = 0;
+		for (String[] array : arrays) {
+			length += array.length;
+		}
+		String[] result = new String[length];
+		length = 0;
+		for (String[] array : arrays) {
+			System.arraycopy(array, 0, result, length, array.length);
+			length += array.length;
+		}
+		return result;
+	}
+
+	/**
+	 * Join <code>byte</code> arrays.
+	 */
+	public static byte[] join(byte[]... arrays) {
+		if (arrays.length == 0) {
+			return new byte[0];
+		}
+		if (arrays.length == 1) {
+			return arrays[0];
+		}
+		int length = 0;
+		for (byte[] array : arrays) {
+			length += array.length;
+		}
+		byte[] result = new byte[length];
+		length = 0;
+		for (byte[] array : arrays) {
+			System.arraycopy(array, 0, result, length, array.length);
+			length += array.length;
+		}
+		return result;
+	}
+
+	/**
+	 * Join <code>char</code> arrays.
+	 */
+	public static char[] join(char[]... arrays) {
+		if (arrays.length == 0) {
+			return new char[0];
+		}
+		if (arrays.length == 1) {
+			return arrays[0];
+		}
+		int length = 0;
+		for (char[] array : arrays) {
+			length += array.length;
+		}
+		char[] result = new char[length];
+		length = 0;
+		for (char[] array : arrays) {
+			System.arraycopy(array, 0, result, length, array.length);
+			length += array.length;
+		}
+		return result;
+	}
+
+	/**
+	 * Join <code>short</code> arrays.
+	 */
+	public static short[] join(short[]... arrays) {
+		if (arrays.length == 0) {
+			return new short[0];
+		}
+		if (arrays.length == 1) {
+			return arrays[0];
+		}
+		int length = 0;
+		for (short[] array : arrays) {
+			length += array.length;
+		}
+		short[] result = new short[length];
+		length = 0;
+		for (short[] array : arrays) {
+			System.arraycopy(array, 0, result, length, array.length);
+			length += array.length;
+		}
+		return result;
+	}
+
+	/**
+	 * Join <code>int</code> arrays.
+	 */
+	public static int[] join(int[]... arrays) {
+		if (arrays.length == 0) {
+			return new int[0];
+		}
+		if (arrays.length == 1) {
+			return arrays[0];
+		}
+		int length = 0;
+		for (int[] array : arrays) {
+			length += array.length;
+		}
+		int[] result = new int[length];
+		length = 0;
+		for (int[] array : arrays) {
+			System.arraycopy(array, 0, result, length, array.length);
+			length += array.length;
+		}
+		return result;
+	}
+
+	/**
+	 * Join <code>long</code> arrays.
+	 */
+	public static long[] join(long[]... arrays) {
+		if (arrays.length == 0) {
+			return new long[0];
+		}
+		if (arrays.length == 1) {
+			return arrays[0];
+		}
+		int length = 0;
+		for (long[] array : arrays) {
+			length += array.length;
+		}
+		long[] result = new long[length];
+		length = 0;
+		for (long[] array : arrays) {
+			System.arraycopy(array, 0, result, length, array.length);
+			length += array.length;
+		}
+		return result;
+	}
+
+	/**
+	 * Join <code>float</code> arrays.
+	 */
+	public static float[] join(float[]... arrays) {
+		if (arrays.length == 0) {
+			return new float[0];
+		}
+		if (arrays.length == 1) {
+			return arrays[0];
+		}
+		int length = 0;
+		for (float[] array : arrays) {
+			length += array.length;
+		}
+		float[] result = new float[length];
+		length = 0;
+		for (float[] array : arrays) {
+			System.arraycopy(array, 0, result, length, array.length);
+			length += array.length;
+		}
+		return result;
+	}
+
+	/**
+	 * Join <code>double</code> arrays.
+	 */
+	public static double[] join(double[]... arrays) {
+		if (arrays.length == 0) {
+			return new double[0];
+		}
+		if (arrays.length == 1) {
+			return arrays[0];
+		}
+		int length = 0;
+		for (double[] array : arrays) {
+			length += array.length;
+		}
+		double[] result = new double[length];
+		length = 0;
+		for (double[] array : arrays) {
+			System.arraycopy(array, 0, result, length, array.length);
+			length += array.length;
+		}
+		return result;
+	}
+
+	/**
+	 * Join <code>boolean</code> arrays.
+	 */
+	public static boolean[] join(boolean[]... arrays) {
+		if (arrays.length == 0) {
+			return new boolean[0];
+		}
+		if (arrays.length == 1) {
+			return arrays[0];
+		}
+		int length = 0;
+		for (boolean[] array : arrays) {
+			length += array.length;
+		}
+		boolean[] result = new boolean[length];
+		length = 0;
+		for (boolean[] array : arrays) {
+			System.arraycopy(array, 0, result, length, array.length);
+			length += array.length;
+		}
+		return result;
+	}
+
+
+	// ---------------------------------------------------------------- resize
+
+	/**
+	 * Resizes an array.
+	 */
+	public static <T> T[] resize(T[] buffer, int newSize) {
+		Class<T> componentType = (Class<T>) buffer.getClass().getComponentType();
+		T[] temp = (T[]) Array.newInstance(componentType, newSize);
+		System.arraycopy(buffer, 0, temp, 0, buffer.length >= newSize ? newSize : buffer.length);
+		return temp;
+	}
+
+
+	/**
+	 * Resizes a <code>String</code> array.
+	 */
+	public static String[] resize(String[] buffer, int newSize) {
+		String[] temp = new String[newSize];
+		System.arraycopy(buffer, 0, temp, 0, buffer.length >= newSize ? newSize : buffer.length);
+		return temp;
+	}
+
+	/**
+	 * Resizes a <code>byte</code> array.
+	 */
+	public static byte[] resize(byte[] buffer, int newSize) {
+		byte[] temp = new byte[newSize];
+		System.arraycopy(buffer, 0, temp, 0, buffer.length >= newSize ? newSize : buffer.length);
+		return temp;
+	}
+
+	/**
+	 * Resizes a <code>char</code> array.
+	 */
+	public static char[] resize(char[] buffer, int newSize) {
+		char[] temp = new char[newSize];
+		System.arraycopy(buffer, 0, temp, 0, buffer.length >= newSize ? newSize : buffer.length);
+		return temp;
+	}
+
+	/**
+	 * Resizes a <code>short</code> array.
+	 */
+	public static short[] resize(short[] buffer, int newSize) {
+		short[] temp = new short[newSize];
+		System.arraycopy(buffer, 0, temp, 0, buffer.length >= newSize ? newSize : buffer.length);
+		return temp;
+	}
+
+	/**
+	 * Resizes a <code>int</code> array.
+	 */
+	public static int[] resize(int[] buffer, int newSize) {
+		int[] temp = new int[newSize];
+		System.arraycopy(buffer, 0, temp, 0, buffer.length >= newSize ? newSize : buffer.length);
+		return temp;
+	}
+
+	/**
+	 * Resizes a <code>long</code> array.
+	 */
+	public static long[] resize(long[] buffer, int newSize) {
+		long[] temp = new long[newSize];
+		System.arraycopy(buffer, 0, temp, 0, buffer.length >= newSize ? newSize : buffer.length);
+		return temp;
+	}
+
+	/**
+	 * Resizes a <code>float</code> array.
+	 */
+	public static float[] resize(float[] buffer, int newSize) {
+		float[] temp = new float[newSize];
+		System.arraycopy(buffer, 0, temp, 0, buffer.length >= newSize ? newSize : buffer.length);
+		return temp;
+	}
+
+	/**
+	 * Resizes a <code>double</code> array.
+	 */
+	public static double[] resize(double[] buffer, int newSize) {
+		double[] temp = new double[newSize];
+		System.arraycopy(buffer, 0, temp, 0, buffer.length >= newSize ? newSize : buffer.length);
+		return temp;
+	}
+
+	/**
+	 * Resizes a <code>boolean</code> array.
+	 */
+	public static boolean[] resize(boolean[] buffer, int newSize) {
+		boolean[] temp = new boolean[newSize];
+		System.arraycopy(buffer, 0, temp, 0, buffer.length >= newSize ? newSize : buffer.length);
+		return temp;
+	}
+
+
+	// ---------------------------------------------------------------- append
+
+	/**
+	 * Appends an element to array.
+	 */
+	public static <T> T[] append(T[] buffer, T newElement) {
+		T[] t = resize(buffer, buffer.length + 1);
+		t[buffer.length] = newElement;
+		return t;
+	}
+
+	/**
+	 * Appends an element to <code>String</code> array.
+	 */
+	public static String[] append(String[] buffer, String newElement) {
+		String[] t = resize(buffer, buffer.length + 1);
+		t[buffer.length] = newElement;
+		return t;
+	}
+
+	/**
+	 * Appends an element to <code>byte</code> array.
+	 */
+	public static byte[] append(byte[] buffer, byte newElement) {
+		byte[] t = resize(buffer, buffer.length + 1);
+		t[buffer.length] = newElement;
+		return t;
+	}
+
+	/**
+	 * Appends an element to <code>char</code> array.
+	 */
+	public static char[] append(char[] buffer, char newElement) {
+		char[] t = resize(buffer, buffer.length + 1);
+		t[buffer.length] = newElement;
+		return t;
+	}
+
+	/**
+	 * Appends an element to <code>short</code> array.
+	 */
+	public static short[] append(short[] buffer, short newElement) {
+		short[] t = resize(buffer, buffer.length + 1);
+		t[buffer.length] = newElement;
+		return t;
+	}
+
+	/**
+	 * Appends an element to <code>int</code> array.
+	 */
+	public static int[] append(int[] buffer, int newElement) {
+		int[] t = resize(buffer, buffer.length + 1);
+		t[buffer.length] = newElement;
+		return t;
+	}
+
+	/**
+	 * Appends an element to <code>long</code> array.
+	 */
+	public static long[] append(long[] buffer, long newElement) {
+		long[] t = resize(buffer, buffer.length + 1);
+		t[buffer.length] = newElement;
+		return t;
+	}
+
+	/**
+	 * Appends an element to <code>float</code> array.
+	 */
+	public static float[] append(float[] buffer, float newElement) {
+		float[] t = resize(buffer, buffer.length + 1);
+		t[buffer.length] = newElement;
+		return t;
+	}
+
+	/**
+	 * Appends an element to <code>double</code> array.
+	 */
+	public static double[] append(double[] buffer, double newElement) {
+		double[] t = resize(buffer, buffer.length + 1);
+		t[buffer.length] = newElement;
+		return t;
+	}
+
+	/**
+	 * Appends an element to <code>boolean</code> array.
+	 */
+	public static boolean[] append(boolean[] buffer, boolean newElement) {
+		boolean[] t = resize(buffer, buffer.length + 1);
+		t[buffer.length] = newElement;
+		return t;
+	}
+
+
+	// ---------------------------------------------------------------- remove
+
+	/**
+	 * Removes sub-array.
+	 */
+	public static <T> T[] remove(T[] buffer, int offset, int length) {
+		Class<T> componentType = (Class<T>) buffer.getClass().getComponentType();
+		return remove(buffer, offset, length, componentType);
+	}
+
+	/**
+	 * Removes sub-array.
+	 */
+	@SuppressWarnings({"unchecked"})
+	public static <T> T[] remove(T[] buffer, int offset, int length, Class<T> componentType) {
+		int len2 = buffer.length - length;
+		T[] temp = (T[]) Array.newInstance(componentType, len2);
+		System.arraycopy(buffer, 0, temp, 0, offset);
+		System.arraycopy(buffer, offset + length, temp, offset, len2 - offset);
+		return temp;
+	}
+
+	/**
+	 * Removes sub-array from <code>String</code> array.
+	 */
+	public static String[] remove(String[] buffer, int offset, int length) {
+		int len2 = buffer.length - length;
+		String[] temp = new String[len2];
+		System.arraycopy(buffer, 0, temp, 0, offset);
+		System.arraycopy(buffer, offset + length, temp, offset, len2 - offset);
+		return temp;
+	}
+
+	/**
+	 * Removes sub-array from <code>byte</code> array.
+	 */
+	public static byte[] remove(byte[] buffer, int offset, int length) {
+		int len2 = buffer.length - length;
+		byte[] temp = new byte[len2];
+		System.arraycopy(buffer, 0, temp, 0, offset);
+		System.arraycopy(buffer, offset + length, temp, offset, len2 - offset);
+		return temp;
+	}
+
+	/**
+	 * Removes sub-array from <code>char</code> array.
+	 */
+	public static char[] remove(char[] buffer, int offset, int length) {
+		int len2 = buffer.length - length;
+		char[] temp = new char[len2];
+		System.arraycopy(buffer, 0, temp, 0, offset);
+		System.arraycopy(buffer, offset + length, temp, offset, len2 - offset);
+		return temp;
+	}
+
+	/**
+	 * Removes sub-array from <code>short</code> array.
+	 */
+	public static short[] remove(short[] buffer, int offset, int length) {
+		int len2 = buffer.length - length;
+		short[] temp = new short[len2];
+		System.arraycopy(buffer, 0, temp, 0, offset);
+		System.arraycopy(buffer, offset + length, temp, offset, len2 - offset);
+		return temp;
+	}
+
+	/**
+	 * Removes sub-array from <code>int</code> array.
+	 */
+	public static int[] remove(int[] buffer, int offset, int length) {
+		int len2 = buffer.length - length;
+		int[] temp = new int[len2];
+		System.arraycopy(buffer, 0, temp, 0, offset);
+		System.arraycopy(buffer, offset + length, temp, offset, len2 - offset);
+		return temp;
+	}
+
+	/**
+	 * Removes sub-array from <code>long</code> array.
+	 */
+	public static long[] remove(long[] buffer, int offset, int length) {
+		int len2 = buffer.length - length;
+		long[] temp = new long[len2];
+		System.arraycopy(buffer, 0, temp, 0, offset);
+		System.arraycopy(buffer, offset + length, temp, offset, len2 - offset);
+		return temp;
+	}
+
+	/**
+	 * Removes sub-array from <code>float</code> array.
+	 */
+	public static float[] remove(float[] buffer, int offset, int length) {
+		int len2 = buffer.length - length;
+		float[] temp = new float[len2];
+		System.arraycopy(buffer, 0, temp, 0, offset);
+		System.arraycopy(buffer, offset + length, temp, offset, len2 - offset);
+		return temp;
+	}
+
+	/**
+	 * Removes sub-array from <code>double</code> array.
+	 */
+	public static double[] remove(double[] buffer, int offset, int length) {
+		int len2 = buffer.length - length;
+		double[] temp = new double[len2];
+		System.arraycopy(buffer, 0, temp, 0, offset);
+		System.arraycopy(buffer, offset + length, temp, offset, len2 - offset);
+		return temp;
+	}
+
+	/**
+	 * Removes sub-array from <code>boolean</code> array.
+	 */
+	public static boolean[] remove(boolean[] buffer, int offset, int length) {
+		int len2 = buffer.length - length;
+		boolean[] temp = new boolean[len2];
+		System.arraycopy(buffer, 0, temp, 0, offset);
+		System.arraycopy(buffer, offset + length, temp, offset, len2 - offset);
+		return temp;
+	}
+
+
+	// ---------------------------------------------------------------- subarray
+
+	/**
+	 * Returns subarray.
+	 */
+	public static <T> T[] subarray(T[] buffer, int offset, int length) {
+		Class<T> componentType = (Class<T>) buffer.getClass().getComponentType();
+		return subarray(buffer, offset, length, componentType);
+	}
+
+	/**
+	 * Returns subarray.
+	 */
+	@SuppressWarnings({"unchecked"})
+	public static <T> T[] subarray(T[] buffer, int offset, int length, Class<T> componentType) {
+		T[] temp = (T[]) Array.newInstance(componentType, length);
+		System.arraycopy(buffer, offset, temp, 0, length);
+		return temp;
+	}
+
+	/**
+	 * Returns subarray.
+	 */
+	public static String[] subarray(String[] buffer, int offset, int length) {
+		String[] temp = new String[length];
+		System.arraycopy(buffer, offset, temp, 0, length);
+		return temp;
+	}
+
+	/**
+	 * Returns subarray.
+	 */
+	public static byte[] subarray(byte[] buffer, int offset, int length) {
+		byte[] temp = new byte[length];
+		System.arraycopy(buffer, offset, temp, 0, length);
+		return temp;
+	}
+
+	/**
+	 * Returns subarray.
+	 */
+	public static char[] subarray(char[] buffer, int offset, int length) {
+		char[] temp = new char[length];
+		System.arraycopy(buffer, offset, temp, 0, length);
+		return temp;
+	}
+
+	/**
+	 * Returns subarray.
+	 */
+	public static short[] subarray(short[] buffer, int offset, int length) {
+		short[] temp = new short[length];
+		System.arraycopy(buffer, offset, temp, 0, length);
+		return temp;
+	}
+
+	/**
+	 * Returns subarray.
+	 */
+	public static int[] subarray(int[] buffer, int offset, int length) {
+		int[] temp = new int[length];
+		System.arraycopy(buffer, offset, temp, 0, length);
+		return temp;
+	}
+
+	/**
+	 * Returns subarray.
+	 */
+	public static long[] subarray(long[] buffer, int offset, int length) {
+		long[] temp = new long[length];
+		System.arraycopy(buffer, offset, temp, 0, length);
+		return temp;
+	}
+
+	/**
+	 * Returns subarray.
+	 */
+	public static float[] subarray(float[] buffer, int offset, int length) {
+		float[] temp = new float[length];
+		System.arraycopy(buffer, offset, temp, 0, length);
+		return temp;
+	}
+
+	/**
+	 * Returns subarray.
+	 */
+	public static double[] subarray(double[] buffer, int offset, int length) {
+		double[] temp = new double[length];
+		System.arraycopy(buffer, offset, temp, 0, length);
+		return temp;
+	}
+
+	/**
+	 * Returns subarray.
+	 */
+	public static boolean[] subarray(boolean[] buffer, int offset, int length) {
+		boolean[] temp = new boolean[length];
+		System.arraycopy(buffer, offset, temp, 0, length);
+		return temp;
+	}
+
+
+	// ---------------------------------------------------------------- insert
+
+	/**
+	 * Inserts one array into another array.
+	 */
+	public static <T> T[] insert(T[] dest, T[] src, int offset) {
+		Class<T> componentType = (Class<T>) dest.getClass().getComponentType();
+		return insert(dest, src, offset, componentType);
+	}
+	/**
+	 * Inserts one element into an array.
+	 */
+	public static <T> T[] insert(T[] dest, T src, int offset) {
+		Class<T> componentType = (Class<T>) dest.getClass().getComponentType();
+		return insert(dest, src, offset, componentType);
+	}
+
+	/**
+	 * Inserts one array into another array.
+	 */
+	@SuppressWarnings({"unchecked"})
+	public static <T> T[] insert(T[] dest, T[] src, int offset, Class componentType) {
+		T[] temp = (T[]) Array.newInstance(componentType, dest.length + src.length);
+		System.arraycopy(dest, 0, temp, 0, offset);
+		System.arraycopy(src, 0, temp, offset, src.length);
+		System.arraycopy(dest, offset, temp, src.length + offset, dest.length - offset);
+		return temp;
+	}
+	/**
+	 * Inserts one element into another array.
+	 */
+	@SuppressWarnings({"unchecked"})
+	public static <T> T[] insert(T[] dest, T src, int offset, Class componentType) {
+		T[] temp = (T[]) Array.newInstance(componentType, dest.length + 1);
+		System.arraycopy(dest, 0, temp, 0, offset);
+		temp[offset] = src;
+		System.arraycopy(dest, offset, temp, offset + 1, dest.length - offset);
+		return temp;
+	}
+
+	/**
+	 * Inserts one array into another <code>String</code> array.
+	 */
+	public static String[] insert(String[] dest, String[] src, int offset) {
+		String[] temp = new String[dest.length + src.length];
+		System.arraycopy(dest, 0, temp, 0, offset);
+		System.arraycopy(src, 0, temp, offset, src.length);
+		System.arraycopy(dest, offset, temp, src.length + offset, dest.length - offset);
+		return temp;
+	}
+
+	/**
+	 * Inserts one element into another <code>String</code> array.
+	 */
+	public static String[] insert(String[] dest, String src, int offset) {
+		String[] temp = new String[dest.length + 1];
+		System.arraycopy(dest, 0, temp, 0, offset);
+		temp[offset] = src;
+		System.arraycopy(dest, offset, temp, offset + 1, dest.length - offset);
+		return temp;
+	}
+
+	/**
+	 * Inserts one array into another <code>byte</code> array.
+	 */
+	public static byte[] insert(byte[] dest, byte[] src, int offset) {
+		byte[] temp = new byte[dest.length + src.length];
+		System.arraycopy(dest, 0, temp, 0, offset);
+		System.arraycopy(src, 0, temp, offset, src.length);
+		System.arraycopy(dest, offset, temp, src.length + offset, dest.length - offset);
+		return temp;
+	}
+
+	/**
+	 * Inserts one element into another <code>byte</code> array.
+	 */
+	public static byte[] insert(byte[] dest, byte src, int offset) {
+		byte[] temp = new byte[dest.length + 1];
+		System.arraycopy(dest, 0, temp, 0, offset);
+		temp[offset] = src;
+		System.arraycopy(dest, offset, temp, offset + 1, dest.length - offset);
+		return temp;
+	}
+
+	/**
+	 * Inserts one array into another <code>char</code> array.
+	 */
+	public static char[] insert(char[] dest, char[] src, int offset) {
+		char[] temp = new char[dest.length + src.length];
+		System.arraycopy(dest, 0, temp, 0, offset);
+		System.arraycopy(src, 0, temp, offset, src.length);
+		System.arraycopy(dest, offset, temp, src.length + offset, dest.length - offset);
+		return temp;
+	}
+
+	/**
+	 * Inserts one element into another <code>char</code> array.
+	 */
+	public static char[] insert(char[] dest, char src, int offset) {
+		char[] temp = new char[dest.length + 1];
+		System.arraycopy(dest, 0, temp, 0, offset);
+		temp[offset] = src;
+		System.arraycopy(dest, offset, temp, offset + 1, dest.length - offset);
+		return temp;
+	}
+
+	/**
+	 * Inserts one array into another <code>short</code> array.
+	 */
+	public static short[] insert(short[] dest, short[] src, int offset) {
+		short[] temp = new short[dest.length + src.length];
+		System.arraycopy(dest, 0, temp, 0, offset);
+		System.arraycopy(src, 0, temp, offset, src.length);
+		System.arraycopy(dest, offset, temp, src.length + offset, dest.length - offset);
+		return temp;
+	}
+
+	/**
+	 * Inserts one element into another <code>short</code> array.
+	 */
+	public static short[] insert(short[] dest, short src, int offset) {
+		short[] temp = new short[dest.length + 1];
+		System.arraycopy(dest, 0, temp, 0, offset);
+		temp[offset] = src;
+		System.arraycopy(dest, offset, temp, offset + 1, dest.length - offset);
+		return temp;
+	}
+
+	/**
+	 * Inserts one array into another <code>int</code> array.
+	 */
+	public static int[] insert(int[] dest, int[] src, int offset) {
+		int[] temp = new int[dest.length + src.length];
+		System.arraycopy(dest, 0, temp, 0, offset);
+		System.arraycopy(src, 0, temp, offset, src.length);
+		System.arraycopy(dest, offset, temp, src.length + offset, dest.length - offset);
+		return temp;
+	}
+
+	/**
+	 * Inserts one element into another <code>int</code> array.
+	 */
+	public static int[] insert(int[] dest, int src, int offset) {
+		int[] temp = new int[dest.length + 1];
+		System.arraycopy(dest, 0, temp, 0, offset);
+		temp[offset] = src;
+		System.arraycopy(dest, offset, temp, offset + 1, dest.length - offset);
+		return temp;
+	}
+
+	/**
+	 * Inserts one array into another <code>long</code> array.
+	 */
+	public static long[] insert(long[] dest, long[] src, int offset) {
+		long[] temp = new long[dest.length + src.length];
+		System.arraycopy(dest, 0, temp, 0, offset);
+		System.arraycopy(src, 0, temp, offset, src.length);
+		System.arraycopy(dest, offset, temp, src.length + offset, dest.length - offset);
+		return temp;
+	}
+
+	/**
+	 * Inserts one element into another <code>long</code> array.
+	 */
+	public static long[] insert(long[] dest, long src, int offset) {
+		long[] temp = new long[dest.length + 1];
+		System.arraycopy(dest, 0, temp, 0, offset);
+		temp[offset] = src;
+		System.arraycopy(dest, offset, temp, offset + 1, dest.length - offset);
+		return temp;
+	}
+
+	/**
+	 * Inserts one array into another <code>float</code> array.
+	 */
+	public static float[] insert(float[] dest, float[] src, int offset) {
+		float[] temp = new float[dest.length + src.length];
+		System.arraycopy(dest, 0, temp, 0, offset);
+		System.arraycopy(src, 0, temp, offset, src.length);
+		System.arraycopy(dest, offset, temp, src.length + offset, dest.length - offset);
+		return temp;
+	}
+
+	/**
+	 * Inserts one element into another <code>float</code> array.
+	 */
+	public static float[] insert(float[] dest, float src, int offset) {
+		float[] temp = new float[dest.length + 1];
+		System.arraycopy(dest, 0, temp, 0, offset);
+		temp[offset] = src;
+		System.arraycopy(dest, offset, temp, offset + 1, dest.length - offset);
+		return temp;
+	}
+
+	/**
+	 * Inserts one array into another <code>double</code> array.
+	 */
+	public static double[] insert(double[] dest, double[] src, int offset) {
+		double[] temp = new double[dest.length + src.length];
+		System.arraycopy(dest, 0, temp, 0, offset);
+		System.arraycopy(src, 0, temp, offset, src.length);
+		System.arraycopy(dest, offset, temp, src.length + offset, dest.length - offset);
+		return temp;
+	}
+
+	/**
+	 * Inserts one element into another <code>double</code> array.
+	 */
+	public static double[] insert(double[] dest, double src, int offset) {
+		double[] temp = new double[dest.length + 1];
+		System.arraycopy(dest, 0, temp, 0, offset);
+		temp[offset] = src;
+		System.arraycopy(dest, offset, temp, offset + 1, dest.length - offset);
+		return temp;
+	}
+
+	/**
+	 * Inserts one array into another <code>boolean</code> array.
+	 */
+	public static boolean[] insert(boolean[] dest, boolean[] src, int offset) {
+		boolean[] temp = new boolean[dest.length + src.length];
+		System.arraycopy(dest, 0, temp, 0, offset);
+		System.arraycopy(src, 0, temp, offset, src.length);
+		System.arraycopy(dest, offset, temp, src.length + offset, dest.length - offset);
+		return temp;
+	}
+
+	/**
+	 * Inserts one element into another <code>boolean</code> array.
+	 */
+	public static boolean[] insert(boolean[] dest, boolean src, int offset) {
+		boolean[] temp = new boolean[dest.length + 1];
+		System.arraycopy(dest, 0, temp, 0, offset);
+		temp[offset] = src;
+		System.arraycopy(dest, offset, temp, offset + 1, dest.length - offset);
+		return temp;
+	}
+
+
+	// ---------------------------------------------------------------- insertAt
+
+	/**
+	 * Inserts one array into another at given offset.
+	 */
+	public static <T> T[] insertAt(T[] dest, T[] src, int offset) {
+		Class<T> componentType = (Class<T>) dest.getClass().getComponentType();
+		return insertAt(dest, src, offset, componentType);
+	}
+
+	/**
+	 * Inserts one array into another at given offset.
+	 */
+	@SuppressWarnings({"unchecked"})
+	public static <T> T[] insertAt(T[] dest, T[] src, int offset, Class componentType) {
+		T[] temp = (T[]) Array.newInstance(componentType, dest.length + src.length - 1);
+		System.arraycopy(dest, 0, temp, 0, offset);
+		System.arraycopy(src, 0, temp, offset, src.length);
+		System.arraycopy(dest, offset + 1, temp, src.length + offset, dest.length - offset - 1);
+		return temp;
+	}
+
+	/**
+	 * Inserts one array into another by replacing specified offset.
+	 */
+	public static String[] insertAt(String[] dest, String[] src, int offset) {
+		String[] temp = new String[dest.length + src.length - 1];
+		System.arraycopy(dest, 0, temp, 0, offset);
+		System.arraycopy(src, 0, temp, offset, src.length);
+		System.arraycopy(dest, offset + 1, temp, src.length + offset, dest.length - offset - 1);
+		return temp;
+	}
+
+	/**
+	 * Inserts one array into another by replacing specified offset.
+	 */
+	public static byte[] insertAt(byte[] dest, byte[] src, int offset) {
+		byte[] temp = new byte[dest.length + src.length - 1];
+		System.arraycopy(dest, 0, temp, 0, offset);
+		System.arraycopy(src, 0, temp, offset, src.length);
+		System.arraycopy(dest, offset + 1, temp, src.length + offset, dest.length - offset - 1);
+		return temp;
+	}
+
+	/**
+	 * Inserts one array into another by replacing specified offset.
+	 */
+	public static char[] insertAt(char[] dest, char[] src, int offset) {
+		char[] temp = new char[dest.length + src.length - 1];
+		System.arraycopy(dest, 0, temp, 0, offset);
+		System.arraycopy(src, 0, temp, offset, src.length);
+		System.arraycopy(dest, offset + 1, temp, src.length + offset, dest.length - offset - 1);
+		return temp;
+	}
+
+	/**
+	 * Inserts one array into another by replacing specified offset.
+	 */
+	public static short[] insertAt(short[] dest, short[] src, int offset) {
+		short[] temp = new short[dest.length + src.length - 1];
+		System.arraycopy(dest, 0, temp, 0, offset);
+		System.arraycopy(src, 0, temp, offset, src.length);
+		System.arraycopy(dest, offset + 1, temp, src.length + offset, dest.length - offset - 1);
+		return temp;
+	}
+
+	/**
+	 * Inserts one array into another by replacing specified offset.
+	 */
+	public static int[] insertAt(int[] dest, int[] src, int offset) {
+		int[] temp = new int[dest.length + src.length - 1];
+		System.arraycopy(dest, 0, temp, 0, offset);
+		System.arraycopy(src, 0, temp, offset, src.length);
+		System.arraycopy(dest, offset + 1, temp, src.length + offset, dest.length - offset - 1);
+		return temp;
+	}
+
+	/**
+	 * Inserts one array into another by replacing specified offset.
+	 */
+	public static long[] insertAt(long[] dest, long[] src, int offset) {
+		long[] temp = new long[dest.length + src.length - 1];
+		System.arraycopy(dest, 0, temp, 0, offset);
+		System.arraycopy(src, 0, temp, offset, src.length);
+		System.arraycopy(dest, offset + 1, temp, src.length + offset, dest.length - offset - 1);
+		return temp;
+	}
+
+	/**
+	 * Inserts one array into another by replacing specified offset.
+	 */
+	public static float[] insertAt(float[] dest, float[] src, int offset) {
+		float[] temp = new float[dest.length + src.length - 1];
+		System.arraycopy(dest, 0, temp, 0, offset);
+		System.arraycopy(src, 0, temp, offset, src.length);
+		System.arraycopy(dest, offset + 1, temp, src.length + offset, dest.length - offset - 1);
+		return temp;
+	}
+
+	/**
+	 * Inserts one array into another by replacing specified offset.
+	 */
+	public static double[] insertAt(double[] dest, double[] src, int offset) {
+		double[] temp = new double[dest.length + src.length - 1];
+		System.arraycopy(dest, 0, temp, 0, offset);
+		System.arraycopy(src, 0, temp, offset, src.length);
+		System.arraycopy(dest, offset + 1, temp, src.length + offset, dest.length - offset - 1);
+		return temp;
+	}
+
+	/**
+	 * Inserts one array into another by replacing specified offset.
+	 */
+	public static boolean[] insertAt(boolean[] dest, boolean[] src, int offset) {
+		boolean[] temp = new boolean[dest.length + src.length - 1];
+		System.arraycopy(dest, 0, temp, 0, offset);
+		System.arraycopy(src, 0, temp, offset, src.length);
+		System.arraycopy(dest, offset + 1, temp, src.length + offset, dest.length - offset - 1);
+		return temp;
+	}
+
+
+	// ---------------------------------------------------------------- convert
+
+
+	/**
+	 * Converts to primitive array.
+	 */
+	public static byte[] values(Byte[] array) {
+		byte[] dest = new byte[array.length];
+		for (int i = 0; i < array.length; i++) {
+			Byte v = array[i];
+			if (v != null) {
+				dest[i] = v.byteValue();
+			}
+		}
+		return dest;
+	}
+	/**
+	 * Converts to object array.
+	 */
+	public static Byte[] valuesOf(byte[] array) {
+		Byte[] dest = new Byte[array.length];
+		for (int i = 0; i < array.length; i++) {
+			dest[i] = Byte.valueOf(array[i]);
+		}
+		return dest;
+	}
+
+
+	/**
+	 * Converts to primitive array.
+	 */
+	public static char[] values(Character[] array) {
+		char[] dest = new char[array.length];
+		for (int i = 0; i < array.length; i++) {
+			Character v = array[i];
+			if (v != null) {
+				dest[i] = v.charValue();
+			}
+		}
+		return dest;
+	}
+	/**
+	 * Converts to object array.
+	 */
+	public static Character[] valuesOf(char[] array) {
+		Character[] dest = new Character[array.length];
+		for (int i = 0; i < array.length; i++) {
+			dest[i] = Character.valueOf(array[i]);
+		}
+		return dest;
+	}
+
+
+	/**
+	 * Converts to primitive array.
+	 */
+	public static short[] values(Short[] array) {
+		short[] dest = new short[array.length];
+		for (int i = 0; i < array.length; i++) {
+			Short v = array[i];
+			if (v != null) {
+				dest[i] = v.shortValue();
+			}
+		}
+		return dest;
+	}
+	/**
+	 * Converts to object array.
+	 */
+	public static Short[] valuesOf(short[] array) {
+		Short[] dest = new Short[array.length];
+		for (int i = 0; i < array.length; i++) {
+			dest[i] = Short.valueOf(array[i]);
+		}
+		return dest;
+	}
+
+
+	/**
+	 * Converts to primitive array.
+	 */
+	public static int[] values(Integer[] array) {
+		int[] dest = new int[array.length];
+		for (int i = 0; i < array.length; i++) {
+			Integer v = array[i];
+			if (v != null) {
+				dest[i] = v.intValue();
+			}
+		}
+		return dest;
+	}
+	/**
+	 * Converts to object array.
+	 */
+	public static Integer[] valuesOf(int[] array) {
+		Integer[] dest = new Integer[array.length];
+		for (int i = 0; i < array.length; i++) {
+			dest[i] = Integer.valueOf(array[i]);
+		}
+		return dest;
+	}
+
+
+	/**
+	 * Converts to primitive array.
+	 */
+	public static long[] values(Long[] array) {
+		long[] dest = new long[array.length];
+		for (int i = 0; i < array.length; i++) {
+			Long v = array[i];
+			if (v != null) {
+				dest[i] = v.longValue();
+			}
+		}
+		return dest;
+	}
+	/**
+	 * Converts to object array.
+	 */
+	public static Long[] valuesOf(long[] array) {
+		Long[] dest = new Long[array.length];
+		for (int i = 0; i < array.length; i++) {
+			dest[i] = Long.valueOf(array[i]);
+		}
+		return dest;
+	}
+
+
+	/**
+	 * Converts to primitive array.
+	 */
+	public static float[] values(Float[] array) {
+		float[] dest = new float[array.length];
+		for (int i = 0; i < array.length; i++) {
+			Float v = array[i];
+			if (v != null) {
+				dest[i] = v.floatValue();
+			}
+		}
+		return dest;
+	}
+	/**
+	 * Converts to object array.
+	 */
+	public static Float[] valuesOf(float[] array) {
+		Float[] dest = new Float[array.length];
+		for (int i = 0; i < array.length; i++) {
+			dest[i] = Float.valueOf(array[i]);
+		}
+		return dest;
+	}
+
+
+	/**
+	 * Converts to primitive array.
+	 */
+	public static double[] values(Double[] array) {
+		double[] dest = new double[array.length];
+		for (int i = 0; i < array.length; i++) {
+			Double v = array[i];
+			if (v != null) {
+				dest[i] = v.doubleValue();
+			}
+		}
+		return dest;
+	}
+	/**
+	 * Converts to object array.
+	 */
+	public static Double[] valuesOf(double[] array) {
+		Double[] dest = new Double[array.length];
+		for (int i = 0; i < array.length; i++) {
+			dest[i] = Double.valueOf(array[i]);
+		}
+		return dest;
+	}
+
+
+	/**
+	 * Converts to primitive array.
+	 */
+	public static boolean[] values(Boolean[] array) {
+		boolean[] dest = new boolean[array.length];
+		for (int i = 0; i < array.length; i++) {
+			Boolean v = array[i];
+			if (v != null) {
+				dest[i] = v.booleanValue();
+			}
+		}
+		return dest;
+	}
+	/**
+	 * Converts to object array.
+	 */
+	public static Boolean[] valuesOf(boolean[] array) {
+		Boolean[] dest = new Boolean[array.length];
+		for (int i = 0; i < array.length; i++) {
+			dest[i] = Boolean.valueOf(array[i]);
+		}
+		return dest;
+	}
+
+
+
+	// ---------------------------------------------------------------- indexof
+
+
+	/**
+	 * Finds the first occurrence of an element in an array.
+	 */
+	public static int indexOf(byte[] array, byte value) {
+		for (int i = 0; i < array.length; i++) {
+			if (array[i] == value) {
+				return i;
+			}
+		}
+		return -1;
+	}
+	/**
+	 * Returns <code>true</code> if an array contains given value.
+	 */
+	public static boolean contains(byte[] array, byte value) {
+		return indexOf(array, value) != -1;
+	}
+	/**
+	 * Finds the first occurrence of given value in an array from specified given position.
+	 */
+	public static int indexOf(byte[] array, byte value, int startIndex) {
+		for (int i = startIndex; i < array.length; i++) {
+			if (array[i] == value) {
+				return i;
+			}
+		}
+		return -1;
+	}
+	/**
+	 * Finds the first occurrence in an array from specified given position and upto given length.
+	 */
+	public static int indexOf(byte[] array, byte value, int startIndex, int endIndex) {
+		for (int i = startIndex; i < endIndex; i++) {
+			if (array[i] == value) {
+				return i;
+			}
+		}
+		return -1;
+	}
+
+	/**
+	 * Finds the first occurrence of an element in an array.
+	 */
+	public static int indexOf(char[] array, char value) {
+		for (int i = 0; i < array.length; i++) {
+			if (array[i] == value) {
+				return i;
+			}
+		}
+		return -1;
+	}
+	/**
+	 * Returns <code>true</code> if an array contains given value.
+	 */
+	public static boolean contains(char[] array, char value) {
+		return indexOf(array, value) != -1;
+	}
+	/**
+	 * Finds the first occurrence of given value in an array from specified given position.
+	 */
+	public static int indexOf(char[] array, char value, int startIndex) {
+		for (int i = startIndex; i < array.length; i++) {
+			if (array[i] == value) {
+				return i;
+			}
+		}
+		return -1;
+	}
+	/**
+	 * Finds the first occurrence in an array from specified given position and upto given length.
+	 */
+	public static int indexOf(char[] array, char value, int startIndex, int endIndex) {
+		for (int i = startIndex; i < endIndex; i++) {
+			if (array[i] == value) {
+				return i;
+			}
+		}
+		return -1;
+	}
+
+	/**
+	 * Finds the first occurrence of an element in an array.
+	 */
+	public static int indexOf(short[] array, short value) {
+		for (int i = 0; i < array.length; i++) {
+			if (array[i] == value) {
+				return i;
+			}
+		}
+		return -1;
+	}
+	/**
+	 * Returns <code>true</code> if an array contains given value.
+	 */
+	public static boolean contains(short[] array, short value) {
+		return indexOf(array, value) != -1;
+	}
+	/**
+	 * Finds the first occurrence of given value in an array from specified given position.
+	 */
+	public static int indexOf(short[] array, short value, int startIndex) {
+		for (int i = startIndex; i < array.length; i++) {
+			if (array[i] == value) {
+				return i;
+			}
+		}
+		return -1;
+	}
+	/**
+	 * Finds the first occurrence in an array from specified given position and upto given length.
+	 */
+	public static int indexOf(short[] array, short value, int startIndex, int endIndex) {
+		for (int i = startIndex; i < endIndex; i++) {
+			if (array[i] == value) {
+				return i;
+			}
+		}
+		return -1;
+	}
+
+	/**
+	 * Finds the first occurrence of an element in an array.
+	 */
+	public static int indexOf(int[] array, int value) {
+		for (int i = 0; i < array.length; i++) {
+			if (array[i] == value) {
+				return i;
+			}
+		}
+		return -1;
+	}
+	/**
+	 * Returns <code>true</code> if an array contains given value.
+	 */
+	public static boolean contains(int[] array, int value) {
+		return indexOf(array, value) != -1;
+	}
+	/**
+	 * Finds the first occurrence of given value in an array from specified given position.
+	 */
+	public static int indexOf(int[] array, int value, int startIndex) {
+		for (int i = startIndex; i < array.length; i++) {
+			if (array[i] == value) {
+				return i;
+			}
+		}
+		return -1;
+	}
+	/**
+	 * Finds the first occurrence in an array from specified given position and upto given length.
+	 */
+	public static int indexOf(int[] array, int value, int startIndex, int endIndex) {
+		for (int i = startIndex; i < endIndex; i++) {
+			if (array[i] == value) {
+				return i;
+			}
+		}
+		return -1;
+	}
+
+	/**
+	 * Finds the first occurrence of an element in an array.
+	 */
+	public static int indexOf(long[] array, long value) {
+		for (int i = 0; i < array.length; i++) {
+			if (array[i] == value) {
+				return i;
+			}
+		}
+		return -1;
+	}
+	/**
+	 * Returns <code>true</code> if an array contains given value.
+	 */
+	public static boolean contains(long[] array, long value) {
+		return indexOf(array, value) != -1;
+	}
+	/**
+	 * Finds the first occurrence of given value in an array from specified given position.
+	 */
+	public static int indexOf(long[] array, long value, int startIndex) {
+		for (int i = startIndex; i < array.length; i++) {
+			if (array[i] == value) {
+				return i;
+			}
+		}
+		return -1;
+	}
+	/**
+	 * Finds the first occurrence in an array from specified given position and upto given length.
+	 */
+	public static int indexOf(long[] array, long value, int startIndex, int endIndex) {
+		for (int i = startIndex; i < endIndex; i++) {
+			if (array[i] == value) {
+				return i;
+			}
+		}
+		return -1;
+	}
+
+	/**
+	 * Finds the first occurrence of an element in an array.
+	 */
+	public static int indexOf(boolean[] array, boolean value) {
+		for (int i = 0; i < array.length; i++) {
+			if (array[i] == value) {
+				return i;
+			}
+		}
+		return -1;
+	}
+	/**
+	 * Returns <code>true</code> if an array contains given value.
+	 */
+	public static boolean contains(boolean[] array, boolean value) {
+		return indexOf(array, value) != -1;
+	}
+	/**
+	 * Finds the first occurrence of given value in an array from specified given position.
+	 */
+	public static int indexOf(boolean[] array, boolean value, int startIndex) {
+		for (int i = startIndex; i < array.length; i++) {
+			if (array[i] == value) {
+				return i;
+			}
+		}
+		return -1;
+	}
+	/**
+	 * Finds the first occurrence in an array from specified given position and upto given length.
+	 */
+	public static int indexOf(boolean[] array, boolean value, int startIndex, int endIndex) {
+		for (int i = startIndex; i < endIndex; i++) {
+			if (array[i] == value) {
+				return i;
+			}
+		}
+		return -1;
+	}
+
+	/**
+	 * Finds the first occurrence of value in <code>float</code> array.
+	 */
+	public static int indexOf(float[] array, float value) {
+		for (int i = 0; i < array.length; i++) {
+			if (Float.compare(array[i], value) == 0) {
+				return i;
+			}
+		}
+		return -1;
+	}
+	/**
+	 * Returns <code>true</code> if <code>float</code> array contains given value.
+	 */
+	public static boolean contains(float[] array, float value) {
+		return indexOf(array, value) != -1;
+	}
+	/**
+	 * Finds the first occurrence of given value in <code>float</code>
+	 * array from specified given position.
+	 */
+	public static int indexOf(float[] array, float value, int startIndex) {
+		for (int i = startIndex; i < array.length; i++) {
+			if (Float.compare(array[i], value) == 0) {
+				return i;
+			}
+		}
+		return -1;
+	}
+	/**
+	 * Finds the first occurrence in <code>float</code> array from specified given position and upto given length.
+	 */
+	public static int indexOf(float[] array, float value, int startIndex, int endIndex) {
+		for (int i = startIndex; i < endIndex; i++) {
+			if (Float.compare(array[i], value) == 0) {
+				return i;
+			}
+		}
+		return -1;
+	}
+
+	/**
+	 * Finds the first occurrence of value in <code>double</code> array.
+	 */
+	public static int indexOf(double[] array, double value) {
+		for (int i = 0; i < array.length; i++) {
+			if (Double.compare(array[i], value) == 0) {
+				return i;
+			}
+		}
+		return -1;
+	}
+	/**
+	 * Returns <code>true</code> if <code>double</code> array contains given value.
+	 */
+	public static boolean contains(double[] array, double value) {
+		return indexOf(array, value) != -1;
+	}
+	/**
+	 * Finds the first occurrence of given value in <code>double</code>
+	 * array from specified given position.
+	 */
+	public static int indexOf(double[] array, double value, int startIndex) {
+		for (int i = startIndex; i < array.length; i++) {
+			if (Double.compare(array[i], value) == 0) {
+				return i;
+			}
+		}
+		return -1;
+	}
+	/**
+	 * Finds the first occurrence in <code>double</code> array from specified given position and upto given length.
+	 */
+	public static int indexOf(double[] array, double value, int startIndex, int endIndex) {
+		for (int i = startIndex; i < endIndex; i++) {
+			if (Double.compare(array[i], value) == 0) {
+				return i;
+			}
+		}
+		return -1;
+	}
+
+	/**
+	 * Finds the first occurrence in an array.
+	 */
+	public static int indexOf(Object[] array, Object value) {
+		for (int i = 0; i < array.length; i++) {
+			if (array[i].equals(value)) {
+				return i;
+			}
+		}
+		return -1;
+	}
+	public static boolean contains(Object[] array, Object value) {
+		return indexOf(array, value) != -1;
+	}
+
+	/**
+	 * Finds the first occurrence in an array from specified given position.
+	 */
+	public static int indexOf(Object[] array, Object value, int startIndex) {
+		for (int i = startIndex; i < array.length; i++) {
+			if (array[i].equals(value)) {
+				return i;
+			}
+		}
+		return -1;
+	}
+	public static boolean contains(Object[] array, Object value, int startIndex) {
+		return indexOf(array, value, startIndex) != -1;
+	}
+
+
+
+
+	// ---------------------------------------------------------------- indexof 2
+
+
+	/**
+	 * Finds the first occurrence in an array.
+	 */
+	public static int indexOf(byte[] array, byte[] sub) {
+		return indexOf(array, sub, 0, array.length);
+	}
+	public static boolean contains(byte[] array, byte[] sub) {
+		return indexOf(array, sub) != -1;
+	}
+
+
+	/**
+	 * Finds the first occurrence in an array from specified given position.
+	 */
+	public static int indexOf(byte[] array, byte[] sub, int startIndex) {
+		return indexOf(array, sub, startIndex, array.length);
+	}
+
+	/**
+	 * Finds the first occurrence in an array from specified given position and upto given length.
+	 */
+	public static int indexOf(byte[] array, byte[] sub, int startIndex, int endIndex) {
+		int sublen = sub.length;
+		if (sublen == 0) {
+			return startIndex;
+		}
+		int total = endIndex - sublen + 1;
+		byte c = sub[0];
+		mainloop:
+		for (int i = startIndex; i < total; i++) {
+			if (array[i] != c) {
+				continue;
+			}
+			int j = 1;
+			int k = i + 1;
+			while (j < sublen) {
+				if (sub[j] != array[k]) {
+					continue mainloop;
+				}
+				j++; k++;
+			}
+			return i;
+		}
+		return -1;
+	}
+
+	/**
+	 * Finds the first occurrence in an array.
+	 */
+	public static int indexOf(char[] array, char[] sub) {
+		return indexOf(array, sub, 0, array.length);
+	}
+	public static boolean contains(char[] array, char[] sub) {
+		return indexOf(array, sub) != -1;
+	}
+
+
+	/**
+	 * Finds the first occurrence in an array from specified given position.
+	 */
+	public static int indexOf(char[] array, char[] sub, int startIndex) {
+		return indexOf(array, sub, startIndex, array.length);
+	}
+
+	/**
+	 * Finds the first occurrence in an array from specified given position and upto given length.
+	 */
+	public static int indexOf(char[] array, char[] sub, int startIndex, int endIndex) {
+		int sublen = sub.length;
+		if (sublen == 0) {
+			return startIndex;
+		}
+		int total = endIndex - sublen + 1;
+		char c = sub[0];
+		mainloop:
+		for (int i = startIndex; i < total; i++) {
+			if (array[i] != c) {
+				continue;
+			}
+			int j = 1;
+			int k = i + 1;
+			while (j < sublen) {
+				if (sub[j] != array[k]) {
+					continue mainloop;
+				}
+				j++; k++;
+			}
+			return i;
+		}
+		return -1;
+	}
+
+	/**
+	 * Finds the first occurrence in an array.
+	 */
+	public static int indexOf(short[] array, short[] sub) {
+		return indexOf(array, sub, 0, array.length);
+	}
+	public static boolean contains(short[] array, short[] sub) {
+		return indexOf(array, sub) != -1;
+	}
+
+
+	/**
+	 * Finds the first occurrence in an array from specified given position.
+	 */
+	public static int indexOf(short[] array, short[] sub, int startIndex) {
+		return indexOf(array, sub, startIndex, array.length);
+	}
+
+	/**
+	 * Finds the first occurrence in an array from specified given position and upto given length.
+	 */
+	public static int indexOf(short[] array, short[] sub, int startIndex, int endIndex) {
+		int sublen = sub.length;
+		if (sublen == 0) {
+			return startIndex;
+		}
+		int total = endIndex - sublen + 1;
+		short c = sub[0];
+		mainloop:
+		for (int i = startIndex; i < total; i++) {
+			if (array[i] != c) {
+				continue;
+			}
+			int j = 1;
+			int k = i + 1;
+			while (j < sublen) {
+				if (sub[j] != array[k]) {
+					continue mainloop;
+				}
+				j++; k++;
+			}
+			return i;
+		}
+		return -1;
+	}
+
+	/**
+	 * Finds the first occurrence in an array.
+	 */
+	public static int indexOf(int[] array, int[] sub) {
+		return indexOf(array, sub, 0, array.length);
+	}
+	public static boolean contains(int[] array, int[] sub) {
+		return indexOf(array, sub) != -1;
+	}
+
+
+	/**
+	 * Finds the first occurrence in an array from specified given position.
+	 */
+	public static int indexOf(int[] array, int[] sub, int startIndex) {
+		return indexOf(array, sub, startIndex, array.length);
+	}
+
+	/**
+	 * Finds the first occurrence in an array from specified given position and upto given length.
+	 */
+	public static int indexOf(int[] array, int[] sub, int startIndex, int endIndex) {
+		int sublen = sub.length;
+		if (sublen == 0) {
+			return startIndex;
+		}
+		int total = endIndex - sublen + 1;
+		int c = sub[0];
+		mainloop:
+		for (int i = startIndex; i < total; i++) {
+			if (array[i] != c) {
+				continue;
+			}
+			int j = 1;
+			int k = i + 1;
+			while (j < sublen) {
+				if (sub[j] != array[k]) {
+					continue mainloop;
+				}
+				j++; k++;
+			}
+			return i;
+		}
+		return -1;
+	}
+
+	/**
+	 * Finds the first occurrence in an array.
+	 */
+	public static int indexOf(long[] array, long[] sub) {
+		return indexOf(array, sub, 0, array.length);
+	}
+	public static boolean contains(long[] array, long[] sub) {
+		return indexOf(array, sub) != -1;
+	}
+
+
+	/**
+	 * Finds the first occurrence in an array from specified given position.
+	 */
+	public static int indexOf(long[] array, long[] sub, int startIndex) {
+		return indexOf(array, sub, startIndex, array.length);
+	}
+
+	/**
+	 * Finds the first occurrence in an array from specified given position and upto given length.
+	 */
+	public static int indexOf(long[] array, long[] sub, int startIndex, int endIndex) {
+		int sublen = sub.length;
+		if (sublen == 0) {
+			return startIndex;
+		}
+		int total = endIndex - sublen + 1;
+		long c = sub[0];
+		mainloop:
+		for (int i = startIndex; i < total; i++) {
+			if (array[i] != c) {
+				continue;
+			}
+			int j = 1;
+			int k = i + 1;
+			while (j < sublen) {
+				if (sub[j] != array[k]) {
+					continue mainloop;
+				}
+				j++; k++;
+			}
+			return i;
+		}
+		return -1;
+	}
+
+	/**
+	 * Finds the first occurrence in an array.
+	 */
+	public static int indexOf(boolean[] array, boolean[] sub) {
+		return indexOf(array, sub, 0, array.length);
+	}
+	public static boolean contains(boolean[] array, boolean[] sub) {
+		return indexOf(array, sub) != -1;
+	}
+
+
+	/**
+	 * Finds the first occurrence in an array from specified given position.
+	 */
+	public static int indexOf(boolean[] array, boolean[] sub, int startIndex) {
+		return indexOf(array, sub, startIndex, array.length);
+	}
+
+	/**
+	 * Finds the first occurrence in an array from specified given position and upto given length.
+	 */
+	public static int indexOf(boolean[] array, boolean[] sub, int startIndex, int endIndex) {
+		int sublen = sub.length;
+		if (sublen == 0) {
+			return startIndex;
+		}
+		int total = endIndex - sublen + 1;
+		boolean c = sub[0];
+		mainloop:
+		for (int i = startIndex; i < total; i++) {
+			if (array[i] != c) {
+				continue;
+			}
+			int j = 1;
+			int k = i + 1;
+			while (j < sublen) {
+				if (sub[j] != array[k]) {
+					continue mainloop;
+				}
+				j++; k++;
+			}
+			return i;
+		}
+		return -1;
+	}
+
+	/**
+	 * Finds the first occurrence in an array.
+	 */
+	public static int indexOf(float[] array, float[] sub) {
+		return indexOf(array, sub, 0, array.length);
+	}
+	public static boolean contains(float[] array, float[] sub) {
+		return indexOf(array, sub) != -1;
+	}
+
+
+	/**
+	 * Finds the first occurrence in an array from specified given position.
+	 */
+	public static int indexOf(float[] array, float[] sub, int startIndex) {
+		return indexOf(array, sub, startIndex, array.length);
+	}
+
+	/**
+	 * Finds the first occurrence in an array from specified given position and upto given length.
+	 */
+	public static int indexOf(float[] array, float[] sub, int startIndex, int endIndex) {
+		int sublen = sub.length;
+		if (sublen == 0) {
+			return startIndex;
+		}
+		int total = endIndex - sublen + 1;
+		float c = sub[0];
+		mainloop:
+		for (int i = startIndex; i < total; i++) {
+			if (Float.compare(array[i], c) != 0) {
+				continue;
+			}
+			int j = 1;
+			int k = i + 1;
+			while (j < sublen) {
+				if (Float.compare(sub[j], array[k]) != 0) {
+					continue mainloop;
+				}
+				j++; k++;
+			}
+			return i;
+		}
+		return -1;
+	}
+
+	/**
+	 * Finds the first occurrence in an array.
+	 */
+	public static int indexOf(double[] array, double[] sub) {
+		return indexOf(array, sub, 0, array.length);
+	}
+	public static boolean contains(double[] array, double[] sub) {
+		return indexOf(array, sub) != -1;
+	}
+
+
+	/**
+	 * Finds the first occurrence in an array from specified given position.
+	 */
+	public static int indexOf(double[] array, double[] sub, int startIndex) {
+		return indexOf(array, sub, startIndex, array.length);
+	}
+
+	/**
+	 * Finds the first occurrence in an array from specified given position and upto given length.
+	 */
+	public static int indexOf(double[] array, double[] sub, int startIndex, int endIndex) {
+		int sublen = sub.length;
+		if (sublen == 0) {
+			return startIndex;
+		}
+		int total = endIndex - sublen + 1;
+		double c = sub[0];
+		mainloop:
+		for (int i = startIndex; i < total; i++) {
+			if (Double.compare(array[i], c) != 0) {
+				continue;
+			}
+			int j = 1;
+			int k = i + 1;
+			while (j < sublen) {
+				if (Double.compare(sub[j], array[k]) != 0) {
+					continue mainloop;
+				}
+				j++; k++;
+			}
+			return i;
+		}
+		return -1;
+	}
+
+
+	// ---------------------------------------------------------------- toString
+
+	/**
+	 * Converts an array to string. Elements are separated by comma.
+	 * Returned string contains no brackets.
+	 */
+	public static String toString(Object[] array) {
+		if (array == null) {
+			return NULL;
+		}
+		if (array.length == 0) {
+			return StringPool.EMPTY;
+		}
+		StringBand sb = new StringBand((array.length << 1) - 1);
+		for (int i = 0; i < array.length; i++) {
+			if (i != 0) {
+				sb.append(StringPool.COMMA);
+			}
+			sb.append(array[i]);
+		}
+		return sb.toString();
+	}
+
+	/**
+	 * Converts an array to string. Elements are separated by comma.
+	 * Returned string contains no brackets.
+	 */
+	public static String toString(String[] array) {
+		if (array == null) {
+			return NULL;
+		}
+		if (array.length == 0) {
+			return StringPool.EMPTY;
+		}
+		StringBand sb = new StringBand((array.length << 1) - 1);
+		for (int i = 0; i < array.length; i++) {
+			if (i != 0) {
+				sb.append(StringPool.COMMA);
+			}
+			sb.append(array[i]);
+		}
+		return sb.toString();
+	}
+
+	/**
+	 * Converts an array to string. Elements are separated by comma.
+	 * Returned string contains no brackets.
+	 */
+	public static String toString(byte[] array) {
+		if (array == null) {
+			return NULL;
+		}
+		if (array.length == 0) {
+			return StringPool.EMPTY;
+		}
+		StringBand sb = new StringBand((array.length << 1) - 1);
+		for (int i = 0; i < array.length; i++) {
+			if (i != 0) {
+				sb.append(StringPool.COMMA);
+			}
+			sb.append(array[i]);
+		}
+		return sb.toString();
+	}
+
+	/**
+	 * Converts an array to string. Elements are separated by comma.
+	 * Returned string contains no brackets.
+	 */
+	public static String toString(char[] array) {
+		if (array == null) {
+			return NULL;
+		}
+		if (array.length == 0) {
+			return StringPool.EMPTY;
+		}
+		StringBand sb = new StringBand((array.length << 1) - 1);
+		for (int i = 0; i < array.length; i++) {
+			if (i != 0) {
+				sb.append(StringPool.COMMA);
+			}
+			sb.append(array[i]);
+		}
+		return sb.toString();
+	}
+
+	/**
+	 * Converts an array to string. Elements are separated by comma.
+	 * Returned string contains no brackets.
+	 */
+	public static String toString(short[] array) {
+		if (array == null) {
+			return NULL;
+		}
+		if (array.length == 0) {
+			return StringPool.EMPTY;
+		}
+		StringBand sb = new StringBand((array.length << 1) - 1);
+		for (int i = 0; i < array.length; i++) {
+			if (i != 0) {
+				sb.append(StringPool.COMMA);
+			}
+			sb.append(array[i]);
+		}
+		return sb.toString();
+	}
+
+	/**
+	 * Converts an array to string. Elements are separated by comma.
+	 * Returned string contains no brackets.
+	 */
+	public static String toString(int[] array) {
+		if (array == null) {
+			return NULL;
+		}
+		if (array.length == 0) {
+			return StringPool.EMPTY;
+		}
+		StringBand sb = new StringBand((array.length << 1) - 1);
+		for (int i = 0; i < array.length; i++) {
+			if (i != 0) {
+				sb.append(StringPool.COMMA);
+			}
+			sb.append(array[i]);
+		}
+		return sb.toString();
+	}
+
+	/**
+	 * Converts an array to string. Elements are separated by comma.
+	 * Returned string contains no brackets.
+	 */
+	public static String toString(long[] array) {
+		if (array == null) {
+			return NULL;
+		}
+		if (array.length == 0) {
+			return StringPool.EMPTY;
+		}
+		StringBand sb = new StringBand((array.length << 1) - 1);
+		for (int i = 0; i < array.length; i++) {
+			if (i != 0) {
+				sb.append(StringPool.COMMA);
+			}
+			sb.append(array[i]);
+		}
+		return sb.toString();
+	}
+
+	/**
+	 * Converts an array to string. Elements are separated by comma.
+	 * Returned string contains no brackets.
+	 */
+	public static String toString(float[] array) {
+		if (array == null) {
+			return NULL;
+		}
+		if (array.length == 0) {
+			return StringPool.EMPTY;
+		}
+		StringBand sb = new StringBand((array.length << 1) - 1);
+		for (int i = 0; i < array.length; i++) {
+			if (i != 0) {
+				sb.append(StringPool.COMMA);
+			}
+			sb.append(array[i]);
+		}
+		return sb.toString();
+	}
+
+	/**
+	 * Converts an array to string. Elements are separated by comma.
+	 * Returned string contains no brackets.
+	 */
+	public static String toString(double[] array) {
+		if (array == null) {
+			return NULL;
+		}
+		if (array.length == 0) {
+			return StringPool.EMPTY;
+		}
+		StringBand sb = new StringBand((array.length << 1) - 1);
+		for (int i = 0; i < array.length; i++) {
+			if (i != 0) {
+				sb.append(StringPool.COMMA);
+			}
+			sb.append(array[i]);
+		}
+		return sb.toString();
+	}
+
+	/**
+	 * Converts an array to string. Elements are separated by comma.
+	 * Returned string contains no brackets.
+	 */
+	public static String toString(boolean[] array) {
+		if (array == null) {
+			return NULL;
+		}
+		if (array.length == 0) {
+			return StringPool.EMPTY;
+		}
+		StringBand sb = new StringBand((array.length << 1) - 1);
+		for (int i = 0; i < array.length; i++) {
+			if (i != 0) {
+				sb.append(StringPool.COMMA);
+			}
+			sb.append(array[i]);
+		}
+		return sb.toString();
+	}
+
+
+	/**
+	 * Converts an array to string array.
+	 */
+	public static String[] toStringArray(Object[] array) {
+		if (array == null) {
+			return null;
+		}
+		String[] result = new String[array.length];
+		for (int i = 0; i < array.length; i++) {
+			result[i] = StringUtil.toString(array[i]);
+		}
+		return result;
+	}
+
+	/**
+	 * Converts an array to string array.
+	 */
+	public static String[] toStringArray(String[] array) {
+		if (array == null) {
+			return null;
+		}
+		String[] result = new String[array.length];
+		for (int i = 0; i < array.length; i++) {
+			result[i] = String.valueOf(array[i]);
+		}
+		return result;
+	}
+
+	/**
+	 * Converts an array to string array.
+	 */
+	public static String[] toStringArray(byte[] array) {
+		if (array == null) {
+			return null;
+		}
+		String[] result = new String[array.length];
+		for (int i = 0; i < array.length; i++) {
+			result[i] = String.valueOf(array[i]);
+		}
+		return result;
+	}
+
+	/**
+	 * Converts an array to string array.
+	 */
+	public static String[] toStringArray(char[] array) {
+		if (array == null) {
+			return null;
+		}
+		String[] result = new String[array.length];
+		for (int i = 0; i < array.length; i++) {
+			result[i] = String.valueOf(array[i]);
+		}
+		return result;
+	}
+
+	/**
+	 * Converts an array to string array.
+	 */
+	public static String[] toStringArray(short[] array) {
+		if (array == null) {
+			return null;
+		}
+		String[] result = new String[array.length];
+		for (int i = 0; i < array.length; i++) {
+			result[i] = String.valueOf(array[i]);
+		}
+		return result;
+	}
+
+	/**
+	 * Converts an array to string array.
+	 */
+	public static String[] toStringArray(int[] array) {
+		if (array == null) {
+			return null;
+		}
+		String[] result = new String[array.length];
+		for (int i = 0; i < array.length; i++) {
+			result[i] = String.valueOf(array[i]);
+		}
+		return result;
+	}
+
+	/**
+	 * Converts an array to string array.
+	 */
+	public static String[] toStringArray(long[] array) {
+		if (array == null) {
+			return null;
+		}
+		String[] result = new String[array.length];
+		for (int i = 0; i < array.length; i++) {
+			result[i] = String.valueOf(array[i]);
+		}
+		return result;
+	}
+
+	/**
+	 * Converts an array to string array.
+	 */
+	public static String[] toStringArray(float[] array) {
+		if (array == null) {
+			return null;
+		}
+		String[] result = new String[array.length];
+		for (int i = 0; i < array.length; i++) {
+			result[i] = String.valueOf(array[i]);
+		}
+		return result;
+	}
+
+	/**
+	 * Converts an array to string array.
+	 */
+	public static String[] toStringArray(double[] array) {
+		if (array == null) {
+			return null;
+		}
+		String[] result = new String[array.length];
+		for (int i = 0; i < array.length; i++) {
+			result[i] = String.valueOf(array[i]);
+		}
+		return result;
+	}
+
+	/**
+	 * Converts an array to string array.
+	 */
+	public static String[] toStringArray(boolean[] array) {
+		if (array == null) {
+			return null;
+		}
+		String[] result = new String[array.length];
+		for (int i = 0; i < array.length; i++) {
+			result[i] = String.valueOf(array[i]);
+		}
+		return result;
+	}
+}
\ No newline at end of file
diff --git a/.pc/07-java11-compatibility.patch/jodd-core/src/main/java/jodd/exception/ExceptionUtil.java b/.pc/07-java11-compatibility.patch/jodd-core/src/main/java/jodd/exception/ExceptionUtil.java
new file mode 100644
index 0000000..470f01d
--- /dev/null
+++ b/.pc/07-java11-compatibility.patch/jodd-core/src/main/java/jodd/exception/ExceptionUtil.java
@@ -0,0 +1,337 @@
+// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+package jodd.exception;
+
+import jodd.io.StreamUtil;
+import jodd.util.StringUtil;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.UndeclaredThrowableException;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * Few exception utilities.
+ */
+public class ExceptionUtil {
+
+	/**
+	 * Returns current stack trace in form of array of stack trace elements.
+	 * First stack trace element is removed.
+	 * Since an exception is thrown internally, this method is slow.
+	 */
+	@SuppressWarnings({"ThrowCaughtLocally"})
+	public static StackTraceElement[] getCurrentStackTrace() {
+		StackTraceElement[] ste = new Exception().getStackTrace();
+		if (ste.length > 1) {
+			StackTraceElement[] result = new StackTraceElement[ste.length - 1];
+			System.arraycopy(ste, 1, result, 0, ste.length - 1);
+			return result;
+		} else {
+			return ste;
+		}
+	}
+
+	// ---------------------------------------------------------------- exception stack trace
+
+	/**
+	 * Returns stack trace filtered by class names.
+	 */
+	public static StackTraceElement[] getStackTrace(Throwable t, String[] allow, String[] deny) {
+		StackTraceElement[] st = t.getStackTrace();
+		ArrayList<StackTraceElement> result = new ArrayList<>(st.length);
+
+		elementLoop:
+		for (StackTraceElement element : st) {
+			String className = element.getClassName();
+			if (allow != null) {
+				boolean validElemenet = false;
+				for (String filter : allow) {
+					if (className.contains(filter)) {
+						validElemenet = true;
+						break;
+					}
+				}
+				if (!validElemenet) {
+					continue;
+				}
+			}
+			if (deny != null) {
+				for (String filter : deny) {
+					if (className.contains(filter)) {
+						continue elementLoop;
+					}
+				}
+			}
+			result.add(element);
+		}
+		st = new StackTraceElement[result.size()];
+		return result.toArray(st);
+	}
+
+	/**
+	 * Returns stack trace chain filtered by class names.
+	 */
+	public static StackTraceElement[][] getStackTraceChain(Throwable t, String[] allow, String[] deny) {
+		ArrayList<StackTraceElement[]> result = new ArrayList<>();
+		while (t != null) {
+			StackTraceElement[] stack = getStackTrace(t, allow, deny);
+			result.add(stack);
+			t = t.getCause();
+		}
+		StackTraceElement[][] allStacks = new StackTraceElement[result.size()][];
+		for (int i = 0; i < allStacks.length; i++) {
+			allStacks[i] = result.get(i);
+		}
+		return allStacks;
+	}
+
+
+	/**
+	 * Returns exception chain starting from top up to root cause.
+	 */
+	public static Throwable[] getExceptionChain(Throwable throwable) {
+		ArrayList<Throwable> list = new ArrayList<>();
+		list.add(throwable);
+		while ((throwable = throwable.getCause()) != null) {
+			list.add(throwable);
+		}
+		Throwable[] result = new Throwable[list.size()];
+		return list.toArray(result);
+	}
+
+
+	// ---------------------------------------------------------------- exception to string
+
+
+	/**
+	 * Prints stack trace into a String.
+	 */
+	public static String exceptionStackTraceToString(Throwable t) {
+		StringWriter sw = new StringWriter();
+		PrintWriter pw = new PrintWriter(sw, true);
+
+		t.printStackTrace(pw);
+
+		StreamUtil.close(pw);
+		StreamUtil.close(sw);
+
+		return sw.toString();
+	}
+
+	/**
+	 * Prints full exception stack trace, from top to root cause, into a String.
+	 */
+	public static String exceptionChainToString(Throwable t) {
+		StringWriter sw = new StringWriter();
+		PrintWriter pw = new PrintWriter(sw, true);
+		while (t != null) {
+			t.printStackTrace(pw);
+			t = t.getCause();
+		}
+
+		StreamUtil.close(pw);
+		StreamUtil.close(sw);
+
+		return sw.toString();
+	}
+
+	/**
+	 * Build a message for the given base message and its cause.
+	 */
+	public static String buildMessage(String message, Throwable cause) {
+		if (cause != null) {
+			cause = getRootCause(cause);
+			StringBuilder buf = new StringBuilder();
+			if (message != null) {
+				buf.append(message).append("; ");
+			}
+			buf.append("<--- ").append(cause);
+			return buf.toString();
+		} else {
+			return message;
+		}
+	}
+
+	// ---------------------------------------------------------------- root cause
+
+	/**
+	 * Introspects the <code>Throwable</code> to obtain the root cause.
+	 * <p>
+	 * This method walks through the exception chain to the last element,
+	 * "root" of the tree, and returns that exception. If no root cause found
+	 * returns provided throwable.
+	 */
+	public static Throwable getRootCause(Throwable throwable) {
+		Throwable cause = throwable.getCause();
+		if (cause == null) {
+			return throwable;
+		}
+		throwable = cause;
+		while ((throwable = throwable.getCause()) != null) {
+			cause = throwable;
+		}
+		return cause;
+	}
+
+	/**
+	 * Finds throwing cause in exception stack. Returns throwable object if cause class is matched.
+	 * Otherwise, returns <code>null</code>.
+	 */
+	@SuppressWarnings({"unchecked"})
+	public static <T extends Throwable> T findCause(Throwable throwable, Class<T> cause) {
+		while (throwable != null) {
+			if (throwable.getClass().equals(cause)) {
+				return (T) throwable;
+			}
+			throwable = throwable.getCause();
+		}
+		return null;
+	}
+
+
+	// ---------------------------------------------------------------- sql
+
+	/**
+     * Rolls up SQL exceptions by taking each proceeding exception
+     * and making it a child of the previous using the <code>setNextException</code>
+     * method of SQLException.
+     */
+	public static SQLException rollupSqlExceptions(Collection<SQLException> exceptions) {
+		SQLException parent = null;
+		for (SQLException exception : exceptions) {
+			if (parent != null) {
+				exception.setNextException(parent);
+			}
+			parent = exception;
+		}
+		return parent;
+	}
+
+	// ---------------------------------------------------------------- misc
+
+	/**
+	 * Throws target of <code>InvocationTargetException</code> if it is exception.
+	 */
+	public static void throwTargetException(InvocationTargetException itex) throws Exception {
+		throw extractTargetException(itex);
+	}
+	public static Exception extractTargetException(InvocationTargetException itex) {
+		Throwable target = itex.getTargetException();
+		return target instanceof Exception ? (Exception) target : itex;
+	}
+
+
+	/**
+	 * Throws checked exceptions in un-checked manner.
+	 * Uses deprecated method.
+	 * @see #throwException(Throwable)
+	 */
+	@SuppressWarnings({"deprecation"})
+	public static void throwExceptionAlt(Throwable throwable) {
+		if (throwable instanceof RuntimeException) {
+			throw (RuntimeException) throwable;
+		}
+		Thread.currentThread().stop(throwable);
+	}
+
+	/**
+	 * Throws checked exceptions in un-checked manner.
+	 * @see #throwException(Throwable) 
+	 */
+	public static void throwException(Throwable throwable) {
+		if (throwable instanceof RuntimeException) {
+			throw (RuntimeException) throwable;
+		}
+		// can't handle these types
+		if ((throwable instanceof IllegalAccessException) || (throwable instanceof InstantiationException)) {
+			throw new IllegalArgumentException(throwable);
+		}
+
+		try {
+			synchronized (ThrowableThrower.class) {
+				ThrowableThrower.throwable = throwable;
+				ThrowableThrower.class.newInstance();
+			}
+		} catch (InstantiationException | IllegalAccessException iex) {
+			throw new RuntimeException(iex);
+		} finally {
+			ThrowableThrower.throwable = null;
+		}
+	}
+
+	/**
+	 * Returns <code>non-null</code> message for a throwable.
+	 */
+	public static String message(Throwable throwable) {
+		String message = throwable.getMessage();
+
+		if (StringUtil.isBlank(message)) {
+			message = throwable.toString();
+		}
+
+		return message;
+	}
+
+	/**
+	 * Wraps exception to {@code RuntimeException}.
+	 */
+	public static RuntimeException wrapRuntime(Throwable throwable) {
+		if (throwable instanceof RuntimeException) {
+			return (RuntimeException) throwable;
+		} else {
+			return new RuntimeException(throwable);
+		}
+	}
+
+	/**
+	 * Unwraps invocation and undeclared exceptions to real cause.
+	 */
+	public static Throwable unwrap(Throwable wrapped) {
+		Throwable unwrapped = wrapped;
+		while (true) {
+			if (unwrapped instanceof InvocationTargetException) {
+				unwrapped = ((InvocationTargetException) unwrapped).getTargetException();
+			}
+			else if (unwrapped instanceof UndeclaredThrowableException) {
+				unwrapped = ((UndeclaredThrowableException) unwrapped).getUndeclaredThrowable();
+			}
+			else {
+				return unwrapped;
+			}
+		}
+	}
+
+	private static class ThrowableThrower {
+		private static Throwable throwable;
+		ThrowableThrower() throws Throwable {
+			throw throwable;
+		}
+	}
+}
diff --git a/.pc/08-servlet-compatibility.patch/jodd-servlet/src/main/java/jodd/servlet/filter/FastByteArrayServletOutputStream.java b/.pc/08-servlet-compatibility.patch/jodd-servlet/src/main/java/jodd/servlet/filter/FastByteArrayServletOutputStream.java
new file mode 100644
index 0000000..3422faf
--- /dev/null
+++ b/.pc/08-servlet-compatibility.patch/jodd-servlet/src/main/java/jodd/servlet/filter/FastByteArrayServletOutputStream.java
@@ -0,0 +1,65 @@
+// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+package jodd.servlet.filter;
+
+import jodd.io.FastByteArrayOutputStream;
+
+import java.io.IOException;
+
+import javax.servlet.ServletOutputStream;
+
+/**
+ * Implementation of <code>ServletOutputStream</code> that buffers
+ * inserted content.
+ */
+public class FastByteArrayServletOutputStream extends ServletOutputStream {
+
+	protected final FastByteArrayOutputStream wrapped;
+
+	public FastByteArrayServletOutputStream() {
+		wrapped = new FastByteArrayOutputStream();
+	}
+
+	/**
+	 * Returns wrapped output stream.
+	 */
+	public FastByteArrayOutputStream getByteArrayStream() {
+		return wrapped;
+	}
+
+	/**
+	 * Writes to wrapped buffer.
+	 */
+	@Override
+	public void write(int i) throws IOException {
+		wrapped.write(i);
+	}
+
+	public void reset() {
+		wrapped.reset();
+	}
+
+}
diff --git a/.pc/08-servlet-compatibility.patch/jodd-servlet/src/main/java/jodd/servlet/filter/GzipResponseStream.java b/.pc/08-servlet-compatibility.patch/jodd-servlet/src/main/java/jodd/servlet/filter/GzipResponseStream.java
new file mode 100644
index 0000000..b33d555
--- /dev/null
+++ b/.pc/08-servlet-compatibility.patch/jodd-servlet/src/main/java/jodd/servlet/filter/GzipResponseStream.java
@@ -0,0 +1,232 @@
+// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+package jodd.servlet.filter;
+
+import java.io.IOException;
+import java.util.zip.GZIPOutputStream;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * GZIP response stream.
+ */
+public class GzipResponseStream extends ServletOutputStream {
+
+	/**
+	 * Constructs a servlet output stream associated with the specified Response.
+	 */
+	public GzipResponseStream(HttpServletResponse response) throws IOException {
+		super();
+		closed = false;
+		this.response = response;
+		this.output = response.getOutputStream();
+	}
+
+	/**
+	 * The threshold number which decides to compress or not.
+	 */
+	protected int compressionThreshold;
+
+	/**
+	 * The buffer through which all of our output bytes are passed.
+	 */
+	protected byte[] buffer;
+
+	/**
+	 * The number of data bytes currently in the buffer.
+	 */
+	protected int bufferCount;
+
+	/**
+	 * The underlying gzip output stream to which we should write data.
+	 */
+	protected GZIPOutputStream gzipstream;
+
+	/**
+	 * Has this stream been closed?
+	 */
+	protected boolean closed;
+
+	/**
+	 * The content length past which we will not write, or -1 if there is no
+	 * defined content length.
+	 */
+	protected int length = -1;
+
+	/**
+	 * The response with which this servlet output stream is associated.
+	 */
+	protected HttpServletResponse response;
+
+	/**
+	 * The underlying servlet output stream to which we should write data.
+	 */
+	protected ServletOutputStream output;
+
+
+	/**
+	 * Sets the compressionThreshold number and create buffer for this size.
+	 */
+	protected void setBuffer(int threshold) {
+		compressionThreshold = threshold;
+		buffer = new byte[compressionThreshold];
+	}
+
+	/**
+	 * Closes this output stream, causing any buffered data to be flushed and any
+	 * further output data to throw an IOException.
+	 */
+	@Override
+	public void close() throws IOException {
+		if (closed) {
+			return;
+		}
+		if (gzipstream != null) {
+			flushToGZip();
+			gzipstream.close();
+			gzipstream = null;
+		} else {
+			if (bufferCount > 0) {
+				output.write(buffer, 0, bufferCount);
+				bufferCount = 0;
+			}
+		}
+		output.close();
+		closed = true;
+	}
+
+
+	/**
+	 * Flushes any buffered data for this output stream, which also causes the
+	 * response to be committed.
+	 */
+	@Override
+	public void flush() throws IOException {
+
+		if (closed) {
+			return;
+		}
+		if (gzipstream != null) {
+			gzipstream.flush();
+		}
+
+	}
+
+	public void flushToGZip() throws IOException {
+		if (bufferCount > 0) {
+			writeToGZip(buffer, 0, bufferCount);
+			bufferCount = 0;
+		}
+	}
+
+	/**
+	 * Writes the specified byte to our output stream.
+	 */
+	@Override
+	public void write(int b) throws IOException {
+
+		if (closed) {
+			throw new IOException("Cannot write to a closed output stream");
+		}
+		if (bufferCount >= buffer.length) {
+			flushToGZip();
+		}
+		buffer[bufferCount++] = (byte) b;
+	}
+
+
+	/**
+	 * Writes <code>b.length</code> bytes from the specified byte array to our
+	 * output stream.
+	 */
+	@Override
+	public void write(byte[] b) throws IOException {
+		write(b, 0, b.length);
+	}
+
+
+	/**
+	 * Writes <code>len</code> bytes from the specified byte array, starting at
+	 * the specified offset, to our output stream.
+	 *
+	 * @param b      byte array containing the bytes to be written
+	 * @param off    zero-relative starting offset of the bytes to be written
+	 * @param len    number of bytes to be written
+	 */
+	@Override
+	public void write(byte[] b, int off, int len) throws IOException {
+
+		if (closed) {
+			throw new IOException("Cannot write to a closed output stream");
+		}
+
+		if (len == 0) {
+			return;
+		}
+
+		// Can we write into buffer ?
+		if (len <= (buffer.length - bufferCount)) {
+			System.arraycopy(b, off, buffer, bufferCount, len);
+			bufferCount += len;
+			return;
+		}
+
+		// There is not enough space in buffer. Flush it ...
+		flushToGZip();
+
+		// ... and try again. Note, that bufferCount = 0 here !
+		if (len <= (buffer.length - bufferCount)) {
+			System.arraycopy(b, off, buffer, bufferCount, len);
+			bufferCount += len;
+			return;
+		}
+
+		// write direct to gzip
+		writeToGZip(b, off, len);
+	}
+
+	/**
+	 * Writes byte array to gzip output stream. Creates new <code>GZIPOutputStream</code>
+	 * if not created yet. Also sets the "Content-Encoding" header.
+	 */
+	public void writeToGZip(byte[] b, int off, int len) throws IOException {
+		if (gzipstream == null) {
+			gzipstream = new GZIPOutputStream(output);
+			response.setHeader("Content-Encoding", "gzip");
+		}
+		gzipstream.write(b, off, len);
+
+	}
+
+	/**
+	 * Returns <code>true</code> if this response stream been closed.
+	 */
+	public boolean closed() {
+		return(this.closed);
+	}
+
+}
diff --git a/.pc/applied-patches b/.pc/applied-patches
new file mode 100644
index 0000000..7fc5d66
--- /dev/null
+++ b/.pc/applied-patches
@@ -0,0 +1,8 @@
+01-disable-coverage-report.patch
+02-disable-bintray.patch
+03-disable-java-check.patch
+04-disable-modules.patch
+05-remove-version-ranges.patch
+06-remove-generated-annotation.patch
+07-java11-compatibility.patch
+08-servlet-compatibility.patch
diff --git a/build.gradle b/build.gradle
index 2911c99..a5a8e6a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -24,7 +24,6 @@
 // POSSIBILITY OF SUCH DAMAGE.
 
 plugins {
-	id "com.jfrog.bintray" version "1.6"
 }
 
 description = '''
@@ -65,8 +64,8 @@ ext {
 		log_logback_core: 	'ch.qos.logback:logback-core:[1.2,1.3)',
 		log_logback_classic: 'ch.qos.logback:logback-classic:[1.2,1.3)',
 		log_jcl:        	'commons-logging:commons-logging:[1.2,1.3)',
-		log_log4j2:		  'org.apache.logging.log4j:log4j-api:[2.8,2.9)',
-		log_log4j2_core:		  'org.apache.logging.log4j:log4j-core:[2.8,2.9)',
+		log_log4j2:		  'org.apache.logging.log4j:log4j-api:debian',
+		log_log4j2_core:		  'org.apache.logging.log4j:log4j-core:debian',
 
 //		asm:			'org.ow2.asm:asm:4.1',
 
@@ -178,9 +177,6 @@ configure(javaModules()) {
 
 	// bintray
 
-	apply plugin: 'com.jfrog.bintray'
-	apply from: "${gradleScriptDir}/bintray.gradle"
-
 	configurations {
 		published
 	}
@@ -192,10 +188,6 @@ configure(javaModules()) {
 		targetCompatibility = '1.8'
 	}
 
-	if (JavaVersion.current() != JavaVersion.VERSION_1_8) {
-		throw new GradleException("This build must be run with Java 8.")
-	}
-
 	tasks.withType(JavaCompile) {
 		options.encoding = 'UTF-8'
 		options.compilerArgs << "-Xlint:-options"
@@ -327,17 +319,6 @@ configure(javaModules()) {
 		}
 	}
 
-	// JaCoCo
-
-	configurations {
-		jacoco
-	}
-
-	dependencies {
-		jacoco lib.jacoco_agent
-	}
-
-
 	// Test: configure all tests, including integration
 
 	tasks.withType(Test) { testTask ->
@@ -353,10 +334,7 @@ configure(javaModules()) {
 			// we need to exclude certain classes as jacoco changes them and may influence testing results
 			List<String> excludes = ['*Test*', '*.?', '*Foo*', '*.data.*', '*.tst*', 'jodd.asm5.*', 'jodd.json.mo*', 'jodd.json.db*', '*.fixtures.*']
 
-			jvmArgs "-javaagent:${configurations.jacoco.asPath}=destfile=${project.buildDir.path}/jacoco/${testTask.name}.exec," +
-					"sessionid=HSServ,append=false,excludes=${excludes.join(':')}",
-			 	'-Djacoco=true',
-			 	'-Xms128m',
+			jvmArgs	'-Xms128m',
 			 	'-Xmx512m',
 			 	'-Duser.timezone=GMT'
 		}
@@ -445,93 +423,10 @@ task testReport(type: TestReport) {
 	reportOn javaBundleModules()*.testIntegration
 }
 
-configurations {
-    jacoco {
-        description 'JARs required for aggregate JacocoReport task.'
-    }
-}
-dependencies {
-    jacoco lib.jacoco_ant
-}
-
-task jacocoReport() {
-	dependsOn testAll
-	group = 'Reporting'
-	description = 'Generates JaCoCo coverage reports for unit tests.'
-
-	ant.taskdef(
-			name:'jacocoreport',
-			classname: 'org.jacoco.ant.ReportTask',
-			classpath: configurations.jacoco.asPath
-	)
-
-	def coverageDir = "$buildDir/reports/coverage"
-	ant.mkdir dir: coverageDir
-
-	inputs.files(javaBundleModulesSources.allSource + javaBundleModulesSources.compileClasspath)
-	outputs.dir file(coverageDir)
-
-	doLast {
-		ant.jacocoreport {
-			executiondata {
-				javaBundleModules().each {submodule ->
-					fileset(dir: "${submodule.buildDir}/jacoco") {
-						include name: '*.exec'
-					}
-				}
-			}
-			structure(name: 'Jodd Coverage Report') {
-				javaBundleModules().each {submodule ->
-					group(name: submodule.name) {
-						classfiles {
-							fileset dir: "${submodule.sourceSets.main.output.classesDir}", excludes: "**/jodd/asm5/**"
-						}
-					}
-				}
-			}
-			html destdir: coverageDir
-			xml destfile: coverageDir + '/jodd-coverage.xml'
-		}
-	}
-}
-
-// Coveralls task should be invoked manually, since we have integration tests
-// that are executed only locally. After the release is build,
-// go to Travis Build History: https://travis-ci.org/oblac/jodd/builds
-// and select one of the jobs, then copy the job id from the URL line.
-// Paste the job id into the .coverals.yml and invoke this task
-// After few minutes, the coverage will appear here:
-// https://coveralls.io/r/oblac/jodd
-task coveralls() {
-	//dependsOn jacocoReport
-	group = 'Reporting'
-	description = 'Submits Jacoco reports to Coveralls.'
-
-	doLast {
-		List<File> targetSrcDirs = new ArrayList<File>()
-
-		for (main in javaBundleModulesSources) {
-			targetSrcDirs += main.java.srcDirs
-		}
-
-		CoverallsReporter coverallsReporter = new CoverallsReporter();
-
-		coverallsReporter.send(targetSrcDirs, file("build/reports/coverage/jodd-coverage.xml"))
-	}
-}
-
 dependencyReport {
 	projects = javaModules()
 }
 
-task bintray {
-	group 'Publishing'
-	description 'Publish all artifcats to bintray'
-
-	dependsOn javaModules().bintrayUpload
-	dependsOn(":jodd-bom:bintrayUpload")
-}
-
 // --- release ----------------------------------------------------------------
 
 task release() {
@@ -542,7 +437,6 @@ task release() {
 	dependsOn testAll
 	dependsOn javadocAll
 	dependsOn testReport
-	dependsOn jacocoReport
 	dependsOn ':distribution:build'
 	dependsOn projectReport
 }
diff --git a/debian/changelog b/debian/changelog
index 28bfc91..f7a2609 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+jodd (3.8.6-1.1) unstable; urgency=medium
+
+  * Non maintainer upload by the Reproducible Builds team.
+  * No source change upload to rebuild on buildd with .buildinfo files.
+
+ -- Holger Levsen <holger@debian.org>  Sat, 09 Jan 2021 11:42:10 +0100
+
 jodd (3.8.6-1) unstable; urgency=medium
 
   * Initial release (Closes: #920771)
diff --git a/jodd-all/build.gradle b/jodd-all/build.gradle
index ce40db7..96e5c61 100644
--- a/jodd-all/build.gradle
+++ b/jodd-all/build.gradle
@@ -7,8 +7,6 @@ apply plugin: 'maven'
 apply plugin: 'signing'
 apply from: "${gradleScriptDir}/provided.gradle"
 apply from: "${gradleScriptDir}/publish-maven.gradle"
-apply plugin: 'com.jfrog.bintray'
-apply from: "${gradleScriptDir}/bintray.gradle"
 
 group = 'org.jodd'
 
@@ -82,4 +80,4 @@ def addAllDependencies(pom) {
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/jodd-bom/build.gradle b/jodd-bom/build.gradle
index ddd19e8..debe527 100644
--- a/jodd-bom/build.gradle
+++ b/jodd-bom/build.gradle
@@ -6,8 +6,6 @@ apply plugin: 'java'    // needs to be java for GRADLE-2427
 apply plugin: 'maven'
 apply plugin: 'signing'
 apply from: "${gradleScriptDir}/publish-maven.gradle"
-apply plugin: 'com.jfrog.bintray'
-apply from: "${gradleScriptDir}/bintray.gradle"
 
 group = 'org.jodd'
 
@@ -53,4 +51,4 @@ def buildBomDependencies(pom) {
 			}
 		}
 	}
-}
\ No newline at end of file
+}
diff --git a/jodd-core/src/main/java/jodd/exception/ExceptionUtil.java b/jodd-core/src/main/java/jodd/exception/ExceptionUtil.java
index 470f01d..49a4440 100644
--- a/jodd-core/src/main/java/jodd/exception/ExceptionUtil.java
+++ b/jodd-core/src/main/java/jodd/exception/ExceptionUtil.java
@@ -258,7 +258,7 @@ public class ExceptionUtil {
 		if (throwable instanceof RuntimeException) {
 			throw (RuntimeException) throwable;
 		}
-		Thread.currentThread().stop(throwable);
+		Thread.currentThread().stop();
 	}
 
 	/**
diff --git a/jodd-core/src/main/java/jodd/util/ArraysUtil.java b/jodd-core/src/main/java/jodd/util/ArraysUtil.java
index cb98090..661701d 100644
--- a/jodd-core/src/main/java/jodd/util/ArraysUtil.java
+++ b/jodd-core/src/main/java/jodd/util/ArraysUtil.java
@@ -25,14 +25,12 @@
 
 package jodd.util;
 
-import javax.annotation.Generated;
 import java.lang.reflect.Array;
 import static jodd.util.StringPool.NULL;
 
 /**
  * Array utilities.
  */
-@Generated("ArraysUtil.py")
 public class ArraysUtil {
 
 
diff --git a/jodd-servlet/src/main/java/jodd/servlet/filter/FastByteArrayServletOutputStream.java b/jodd-servlet/src/main/java/jodd/servlet/filter/FastByteArrayServletOutputStream.java
index 3422faf..2d234cd 100644
--- a/jodd-servlet/src/main/java/jodd/servlet/filter/FastByteArrayServletOutputStream.java
+++ b/jodd-servlet/src/main/java/jodd/servlet/filter/FastByteArrayServletOutputStream.java
@@ -27,9 +27,9 @@ package jodd.servlet.filter;
 
 import jodd.io.FastByteArrayOutputStream;
 
-import java.io.IOException;
-
 import javax.servlet.ServletOutputStream;
+import javax.servlet.WriteListener;
+import java.io.IOException;
 
 /**
  * Implementation of <code>ServletOutputStream</code> that buffers
@@ -43,6 +43,15 @@ public class FastByteArrayServletOutputStream extends ServletOutputStream {
 		wrapped = new FastByteArrayOutputStream();
 	}
 
+	@Override
+	public boolean isReady() {
+		return false;
+	}
+
+	@Override
+	public void setWriteListener(WriteListener writeListener) {
+	}
+
 	/**
 	 * Returns wrapped output stream.
 	 */
diff --git a/jodd-servlet/src/main/java/jodd/servlet/filter/GzipResponseStream.java b/jodd-servlet/src/main/java/jodd/servlet/filter/GzipResponseStream.java
index b33d555..d69c2f1 100644
--- a/jodd-servlet/src/main/java/jodd/servlet/filter/GzipResponseStream.java
+++ b/jodd-servlet/src/main/java/jodd/servlet/filter/GzipResponseStream.java
@@ -25,11 +25,11 @@
 
 package jodd.servlet.filter;
 
-import java.io.IOException;
-import java.util.zip.GZIPOutputStream;
-
 import javax.servlet.ServletOutputStream;
+import javax.servlet.WriteListener;
 import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.zip.GZIPOutputStream;
 
 /**
  * GZIP response stream.
@@ -96,6 +96,16 @@ public class GzipResponseStream extends ServletOutputStream {
 		buffer = new byte[compressionThreshold];
 	}
 
+	@Override
+	public boolean isReady() {
+		return output.isReady();
+	}
+
+	@Override
+	public void setWriteListener(WriteListener writeListener) {
+		output.setWriteListener(writeListener);
+	}
+
 	/**
 	 * Closes this output stream, causing any buffered data to be flushed and any
 	 * further output data to throw an IOException.
diff --git a/settings.gradle b/settings.gradle
index c0f5b15..170ae9f 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,25 +1,25 @@
-include 'distribution'
+//include 'distribution'
 
-include 'jodd-all'
+//include 'jodd-all'
 include 'jodd-bean'
 include 'jodd-core'
-include 'jodd-db'
-include 'jodd-decora'
-include 'jodd-htmlstapler'
+//include 'jodd-db'
+//include 'jodd-decora'
+//include 'jodd-htmlstapler'
 include 'jodd-http'
-include 'jodd-joy'
-include 'jodd-json'
-include 'jodd-jtx'
+//include 'jodd-joy'
+//include 'jodd-json'
+//include 'jodd-jtx'
 include 'jodd-lagarto'
 include 'jodd-log'
-include 'jodd-madvoc'
-include 'jodd-mail'
-include 'jodd-petite'
+//include 'jodd-madvoc'
+//include 'jodd-mail'
+//include 'jodd-petite'
 include 'jodd-props'
-include 'jodd-proxetta'
+//include 'jodd-proxetta'
 include 'jodd-servlet'
-include 'jodd-swingspy'
+//include 'jodd-swingspy'
 include 'jodd-upload'
-include 'jodd-vtor'
+//include 'jodd-vtor'
 
-include 'jodd-bom'
\ No newline at end of file
+//include 'jodd-bom'

Run locally

More details

Full run details