--- a/.hgignore Mon Mar 04 17:06:42 2019 +0100
+++ b/.hgignore Mon Mar 04 20:15:24 2019 +0100
@@ -3,8 +3,8 @@
*~
temp/*
-java/sql-dk/data/info/globalcode/sql/dk/version.txt
-java/sql-dk/data/info/globalcode/sql/dk/help.txt
+java/sql-dk/src/main/resources/info/globalcode/sql/dk/version.txt
+java/sql-dk/src/main/resources/info/globalcode/sql/dk/help.txt
syntax: regexp
--- a/distributions/debian/build.sh Mon Mar 04 17:06:42 2019 +0100
+++ b/distributions/debian/build.sh Mon Mar 04 20:15:24 2019 +0100
@@ -29,12 +29,12 @@
cp ../../../xml/config.xsd config.xsd &&
cp ../../../xml/config.rnc config.rnc &&
cp ../../../xml/config.xsl config.xsl &&
-cp ../../../java/sql-dk/dist/sql-dk.jar sql-dk.jar &&
+cp ../../../java/sql-dk/target/sql-dk-*.jar sql-dk.jar &&
cp ../../../java/jdbc-loopback-driver/target/jdbc-loopback-driver-*.jar jdbc-loopback-driver.jar &&
-cp ../../../java/sql-dk/dist/bash-completion.sh SQL-DK && # TODO: should be sql-dk – name conflict with sql-dk in /usr/bin/ (equivs bug)
+cp ../../../java/sql-dk/target/bash-completion.sh SQL-DK && # TODO: should be sql-dk – name conflict with sql-dk in /usr/bin/ (equivs bug)
chmod 755 sql-dk &&
-chmod 755 SQL-DK &&
+chmod 644 SQL-DK &&
EMAIL=`echo c3FsLWRrLmRlYmlhbkBwdWIuZnJhbnRvdm8uY3oK | base64 -d` &&
NAME="Ing. František Kučera <$EMAIL>" &&
--- a/distributions/fedora/sql-dk.spec Mon Mar 04 17:06:42 2019 +0100
+++ b/distributions/fedora/sql-dk.spec Mon Mar 04 20:15:24 2019 +0100
@@ -71,9 +71,9 @@
cp ../../../../xml/config.xsd ${RPM_BUILD_ROOT}/usr/share/doc/sql-dk/
cp ../../../../xml/config.rnc ${RPM_BUILD_ROOT}/usr/share/doc/sql-dk/
cp ../../../../xml/config.xsl ${RPM_BUILD_ROOT}/usr/share/doc/sql-dk/
-cp ../../../../java/sql-dk/dist/sql-dk.jar ${RPM_BUILD_ROOT}/usr/share/sql-dk/
-cp ../../../../java/jdbc-loopback-driver/dist/jdbc-loopback-driver.jar ${RPM_BUILD_ROOT}/usr/share/sql-dk/
-cp ../../../../java/sql-dk/dist/bash-completion.sh ${RPM_BUILD_ROOT}/etc/bash_completion.d/sql-dk
+cp ../../../../java/sql-dk/target/sql-dk-*.jar ${RPM_BUILD_ROOT}/usr/share/sql-dk/
+cp ../../../../java/jdbc-loopback-driver/target/jdbc-loopback-driver-*.jar ${RPM_BUILD_ROOT}/usr/share/sql-dk/
+cp ../../../../java/sql-dk/target/bash-completion.sh ${RPM_BUILD_ROOT}/etc/bash_completion.d/sql-dk
%files
%defattr(-,root,root)
@@ -81,4 +81,3 @@
/usr/share/sql-dk/*
/usr/share/doc/sql-dk/*
/etc/bash_completion.d/*
-
--- a/java/sql-dk/bash-completion.sh Mon Mar 04 17:06:42 2019 +0100
+++ b/java/sql-dk/bash-completion.sh Mon Mar 04 20:15:24 2019 +0100
@@ -1,8 +1,8 @@
#!/bin/bash
cat \
- src/info/globalcode/sql/dk/Constants.java \
- src/info/globalcode/sql/dk/formatting/* \
- src/info/globalcode/sql/dk/CLIParser.java \
+ src/main/java/info/globalcode/sql/dk/Constants.java \
+ src/main/java/info/globalcode/sql/dk/formatting/* \
+ src/main/java/info/globalcode/sql/dk/CLIParser.java \
| ../../scripts/bash_completion.pl
--- a/java/sql-dk/build.xml Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,102 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- SQL-DK
- Copyright © 2013 František Kučera (frantovo.cz)
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
--->
-
-<!-- You may freely edit this file. See commented blocks below for -->
-<!-- some examples of how to customize the build. -->
-<!-- (If you delete it and reopen the project it will be recreated.) -->
-<!-- By default, only the Clean and Build commands use this build script. -->
-<!-- Commands such as Run, Debug, and Test only use this build script if -->
-<!-- the Compile on Save feature is turned off for the project. -->
-<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
-<!-- in the project's Project Properties dialog box.-->
-<project name="sql-dk" default="default" basedir=".">
- <description>Builds, tests, and runs the project sql-dk.</description>
- <import file="nbproject/build-impl.xml"/>
- <!--
-
- There exist several targets which are by default empty and which can be
- used for execution of your tasks. These targets are usually executed
- before and after some main targets. They are:
-
- -pre-init: called before initialization of project properties
- -post-init: called after initialization of project properties
- -pre-compile: called before javac compilation
- -post-compile: called after javac compilation
- -pre-compile-single: called before javac compilation of single file
- -post-compile-single: called after javac compilation of single file
- -pre-compile-test: called before javac compilation of JUnit tests
- -post-compile-test: called after javac compilation of JUnit tests
- -pre-compile-test-single: called before javac compilation of single JUnit test
- -post-compile-test-single: called after javac compilation of single JUunit test
- -pre-jar: called before JAR building
- -post-jar: called after JAR building
- -post-clean: called after cleaning build products
-
- (Targets beginning with '-' are not intended to be called on their own.)
-
- Example of inserting an obfuscator after compilation could look like this:
-
- <target name="-post-compile">
- <obfuscate>
- <fileset dir="${build.classes.dir}"/>
- </obfuscate>
- </target>
-
- For list of available properties check the imported
- nbproject/build-impl.xml file.
-
-
- Another way to customize the build is by overriding existing main targets.
- The targets of interest are:
-
- -init-macrodef-javac: defines macro for javac compilation
- -init-macrodef-junit: defines macro for junit execution
- -init-macrodef-debug: defines macro for class debugging
- -init-macrodef-java: defines macro for class execution
- -do-jar-with-manifest: JAR building (if you are using a manifest)
- -do-jar-without-manifest: JAR building (if you are not using a manifest)
- run: execution of project
- -javadoc-build: Javadoc generation
- test-report: JUnit report generation
-
- An example of overriding the target for project execution could look like this:
-
- <target name="run" depends="sql-dk-impl.jar">
- <exec dir="bin" executable="launcher.exe">
- <arg file="${dist.jar}"/>
- </exec>
- </target>
-
- Notice that the overridden target depends on the jar target and not only on
- the compile target as the regular run target does. Again, for a list of available
- properties which you can use, check the target you are overriding in the
- nbproject/build-impl.xml file.
-
- -->
-
- <target name="-pre-compile">
- <exec executable="./version-info.sh" output="data/info/globalcode/sql/dk/version.txt"/>
- <exec executable="./help-generator.sh" output="data/info/globalcode/sql/dk/help.txt"/>
- </target>
-
- <target name="-post-jar">
- <exec executable="./bash-completion.sh" output="dist/bash-completion.sh"/>
- </target>
-
-</project>
--- a/java/sql-dk/data/info/globalcode/sql/dk/example-config.xml Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-../../../../../../../xml/config.xml
\ No newline at end of file
--- a/java/sql-dk/data/info/globalcode/sql/dk/formatter/XhtmlFormatter.css Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-body {
- font-family: sans-serif;
- font-size: 16px;
- padding-left: 16px;
- padding-right: 16px;
-}
-
-pre {
- background-color: #ddd;
- padding: 6px;
- border-radius: 4px;
- overflow: auto;
-
- -moz-tab-size: 4;
- -o-tab-size: 4;
- tab-size: 4;
-}
-
-table {
- border-collapse:collapse;
- box-shadow: 3px 3px 3px grey;
- margin-top: 10px;
- margin-bottom: 20px;
-}
-td, th {
- border: 1px solid black;
- padding-top: 4px;
- padding-bottom: 4px;
- padding-left: 6px;
- padding-right: 6px;
- font-weight: normal;
-}
-td.number {
- text-align: right;
-}
-td.boolean {
- text-align: right;
-}
-thead tr {
- background: #ddd;
- color:black;
-}
-tbody tr:hover {
- background-color: #eee;
- color:black;
-}
-
-table ul {
- margin: 0px;
-}
-
-table li {
- padding-right: 10px;
-}
--- a/java/sql-dk/data/info/globalcode/sql/dk/license.txt Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-../../../../../../../license/gpl.txt
\ No newline at end of file
--- a/java/sql-dk/help-generator.sh Mon Mar 04 17:06:42 2019 +0100
+++ b/java/sql-dk/help-generator.sh Mon Mar 04 20:15:24 2019 +0100
@@ -1,7 +1,7 @@
#!/bin/bash
cat \
- src/info/globalcode/sql/dk/CLIParser.java \
- src/info/globalcode/sql/dk/CLIStarter.java \
+ src/main/java/info/globalcode/sql/dk/CLIParser.java \
+ src/main/java/info/globalcode/sql/dk/CLIStarter.java \
| ../../scripts/help_generator.pl
--- a/java/sql-dk/manifest.mf Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,3 +0,0 @@
-Manifest-Version: 1.0
-X-COMMENT: Main-Class will be added automatically by build
-
--- a/java/sql-dk/nbproject/build-impl.xml Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,1429 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-*** GENERATED FROM project.xml - DO NOT EDIT ***
-*** EDIT ../build.xml INSTEAD ***
-
-For the purpose of easier reading the script
-is divided into following sections:
-
- - initialization
- - compilation
- - jar
- - execution
- - debugging
- - javadoc
- - test compilation
- - test execution
- - test debugging
- - applet
- - cleanup
-
- -->
-<project xmlns:j2seproject1="http://www.netbeans.org/ns/j2se-project/1" xmlns:j2seproject3="http://www.netbeans.org/ns/j2se-project/3" xmlns:jaxrpc="http://www.netbeans.org/ns/j2se-project/jax-rpc" basedir=".." default="default" name="sql-dk-impl">
- <fail message="Please build using Ant 1.8.0 or higher.">
- <condition>
- <not>
- <antversion atleast="1.8.0"/>
- </not>
- </condition>
- </fail>
- <target depends="test,jar,javadoc" description="Build and test whole project." name="default"/>
- <!--
- ======================
- INITIALIZATION SECTION
- ======================
- -->
- <target name="-pre-init">
- <!-- Empty placeholder for easier customization. -->
- <!-- You can override this target in the ../build.xml file. -->
- </target>
- <target depends="-pre-init" name="-init-private">
- <property file="nbproject/private/config.properties"/>
- <property file="nbproject/private/configs/${config}.properties"/>
- <property file="nbproject/private/private.properties"/>
- </target>
- <target depends="-pre-init,-init-private" name="-init-user">
- <property file="${user.properties.file}"/>
- <!-- The two properties below are usually overridden -->
- <!-- by the active platform. Just a fallback. -->
- <property name="default.javac.source" value="1.6"/>
- <property name="default.javac.target" value="1.6"/>
- </target>
- <target depends="-pre-init,-init-private,-init-user" name="-init-project">
- <property file="nbproject/configs/${config}.properties"/>
- <property file="nbproject/project.properties"/>
- </target>
- <target depends="-pre-init,-init-private,-init-user,-init-project,-init-macrodef-property" name="-do-init">
- <property name="platform.java" value="${java.home}/bin/java"/>
- <available file="${manifest.file}" property="manifest.available"/>
- <condition property="splashscreen.available">
- <and>
- <not>
- <equals arg1="${application.splash}" arg2="" trim="true"/>
- </not>
- <available file="${application.splash}"/>
- </and>
- </condition>
- <condition property="main.class.available">
- <and>
- <isset property="main.class"/>
- <not>
- <equals arg1="${main.class}" arg2="" trim="true"/>
- </not>
- </and>
- </condition>
- <condition property="profile.available">
- <and>
- <isset property="javac.profile"/>
- <length length="0" string="${javac.profile}" when="greater"/>
- <matches pattern="((1\.[89])|9)(\..*)?" string="${javac.source}"/>
- </and>
- </condition>
- <condition property="do.archive">
- <or>
- <not>
- <istrue value="${jar.archive.disabled}"/>
- </not>
- <istrue value="${not.archive.disabled}"/>
- </or>
- </condition>
- <condition property="do.mkdist">
- <and>
- <isset property="do.archive"/>
- <isset property="libs.CopyLibs.classpath"/>
- <not>
- <istrue value="${mkdist.disabled}"/>
- </not>
- </and>
- </condition>
- <condition property="do.archive+manifest.available">
- <and>
- <isset property="manifest.available"/>
- <istrue value="${do.archive}"/>
- </and>
- </condition>
- <condition property="do.archive+main.class.available">
- <and>
- <isset property="main.class.available"/>
- <istrue value="${do.archive}"/>
- </and>
- </condition>
- <condition property="do.archive+splashscreen.available">
- <and>
- <isset property="splashscreen.available"/>
- <istrue value="${do.archive}"/>
- </and>
- </condition>
- <condition property="do.archive+profile.available">
- <and>
- <isset property="profile.available"/>
- <istrue value="${do.archive}"/>
- </and>
- </condition>
- <condition property="have.tests">
- <or>
- <available file="${test.src.dir}"/>
- </or>
- </condition>
- <condition property="have.sources">
- <or>
- <available file="${src.dir}"/>
- <available file="${src.data.dir}"/>
- </or>
- </condition>
- <condition property="netbeans.home+have.tests">
- <and>
- <isset property="netbeans.home"/>
- <isset property="have.tests"/>
- </and>
- </condition>
- <condition property="no.javadoc.preview">
- <and>
- <isset property="javadoc.preview"/>
- <isfalse value="${javadoc.preview}"/>
- </and>
- </condition>
- <property name="run.jvmargs" value=""/>
- <property name="run.jvmargs.ide" value=""/>
- <property name="javac.compilerargs" value=""/>
- <property name="work.dir" value="${basedir}"/>
- <condition property="no.deps">
- <and>
- <istrue value="${no.dependencies}"/>
- </and>
- </condition>
- <property name="javac.debug" value="true"/>
- <property name="javadoc.preview" value="true"/>
- <property name="application.args" value=""/>
- <property name="source.encoding" value="${file.encoding}"/>
- <property name="runtime.encoding" value="${source.encoding}"/>
- <property name="manifest.encoding" value="${source.encoding}"/>
- <condition property="javadoc.encoding.used" value="${javadoc.encoding}">
- <and>
- <isset property="javadoc.encoding"/>
- <not>
- <equals arg1="${javadoc.encoding}" arg2=""/>
- </not>
- </and>
- </condition>
- <property name="javadoc.encoding.used" value="${source.encoding}"/>
- <property name="includes" value="**"/>
- <property name="excludes" value=""/>
- <property name="do.depend" value="false"/>
- <condition property="do.depend.true">
- <istrue value="${do.depend}"/>
- </condition>
- <path id="endorsed.classpath.path" path="${endorsed.classpath}"/>
- <condition else="" property="endorsed.classpath.cmd.line.arg" value="-Xbootclasspath/p:'${toString:endorsed.classpath.path}'">
- <and>
- <isset property="endorsed.classpath"/>
- <not>
- <equals arg1="${endorsed.classpath}" arg2="" trim="true"/>
- </not>
- </and>
- </condition>
- <condition else="" property="javac.profile.cmd.line.arg" value="-profile ${javac.profile}">
- <isset property="profile.available"/>
- </condition>
- <condition else="false" property="jdkBug6558476">
- <and>
- <matches pattern="1\.[56]" string="${java.specification.version}"/>
- <not>
- <os family="unix"/>
- </not>
- </and>
- </condition>
- <condition else="false" property="javac.fork">
- <or>
- <istrue value="${jdkBug6558476}"/>
- <istrue value="${javac.external.vm}"/>
- </or>
- </condition>
- <property name="jar.index" value="false"/>
- <property name="jar.index.metainf" value="${jar.index}"/>
- <property name="copylibs.rebase" value="true"/>
- <available file="${meta.inf.dir}/persistence.xml" property="has.persistence.xml"/>
- <condition property="junit.available">
- <or>
- <available classname="org.junit.Test" classpath="${run.test.classpath}"/>
- <available classname="junit.framework.Test" classpath="${run.test.classpath}"/>
- </or>
- </condition>
- <condition property="testng.available">
- <available classname="org.testng.annotations.Test" classpath="${run.test.classpath}"/>
- </condition>
- <condition property="junit+testng.available">
- <and>
- <istrue value="${junit.available}"/>
- <istrue value="${testng.available}"/>
- </and>
- </condition>
- <condition else="testng" property="testng.mode" value="mixed">
- <istrue value="${junit+testng.available}"/>
- </condition>
- <condition else="" property="testng.debug.mode" value="-mixed">
- <istrue value="${junit+testng.available}"/>
- </condition>
- <property name="java.failonerror" value="true"/>
- </target>
- <target name="-post-init">
- <!-- Empty placeholder for easier customization. -->
- <!-- You can override this target in the ../build.xml file. -->
- </target>
- <target depends="-pre-init,-init-private,-init-user,-init-project,-do-init" name="-init-check">
- <fail unless="src.dir">Must set src.dir</fail>
- <fail unless="src.data.dir">Must set src.data.dir</fail>
- <fail unless="test.src.dir">Must set test.src.dir</fail>
- <fail unless="build.dir">Must set build.dir</fail>
- <fail unless="dist.dir">Must set dist.dir</fail>
- <fail unless="build.classes.dir">Must set build.classes.dir</fail>
- <fail unless="dist.javadoc.dir">Must set dist.javadoc.dir</fail>
- <fail unless="build.test.classes.dir">Must set build.test.classes.dir</fail>
- <fail unless="build.test.results.dir">Must set build.test.results.dir</fail>
- <fail unless="build.classes.excludes">Must set build.classes.excludes</fail>
- <fail unless="dist.jar">Must set dist.jar</fail>
- </target>
- <target name="-init-macrodef-property">
- <macrodef name="property" uri="http://www.netbeans.org/ns/j2se-project/1">
- <attribute name="name"/>
- <attribute name="value"/>
- <sequential>
- <property name="@{name}" value="${@{value}}"/>
- </sequential>
- </macrodef>
- </target>
- <target depends="-init-ap-cmdline-properties" if="ap.supported.internal" name="-init-macrodef-javac-with-processors">
- <macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3">
- <attribute default="${src.dir}:${src.data.dir}" name="srcdir"/>
- <attribute default="${build.classes.dir}" name="destdir"/>
- <attribute default="${javac.classpath}" name="classpath"/>
- <attribute default="${javac.processorpath}" name="processorpath"/>
- <attribute default="${build.generated.sources.dir}/ap-source-output" name="apgeneratedsrcdir"/>
- <attribute default="${includes}" name="includes"/>
- <attribute default="${excludes}" name="excludes"/>
- <attribute default="${javac.debug}" name="debug"/>
- <attribute default="${empty.dir}" name="sourcepath"/>
- <attribute default="${empty.dir}" name="gensrcdir"/>
- <element name="customize" optional="true"/>
- <sequential>
- <property location="${build.dir}/empty" name="empty.dir"/>
- <mkdir dir="${empty.dir}"/>
- <mkdir dir="@{apgeneratedsrcdir}"/>
- <javac debug="@{debug}" deprecation="${javac.deprecation}" destdir="@{destdir}" encoding="${source.encoding}" excludes="@{excludes}" fork="${javac.fork}" includeantruntime="false" includes="@{includes}" source="${javac.source}" sourcepath="@{sourcepath}" srcdir="@{srcdir}" target="${javac.target}" tempdir="${java.io.tmpdir}">
- <src>
- <dirset dir="@{gensrcdir}" erroronmissingdir="false">
- <include name="*"/>
- </dirset>
- </src>
- <classpath>
- <path path="@{classpath}"/>
- </classpath>
- <compilerarg line="${endorsed.classpath.cmd.line.arg}"/>
- <compilerarg line="${javac.profile.cmd.line.arg}"/>
- <compilerarg line="${javac.compilerargs}"/>
- <compilerarg value="-processorpath"/>
- <compilerarg path="@{processorpath}:${empty.dir}"/>
- <compilerarg line="${ap.processors.internal}"/>
- <compilerarg line="${annotation.processing.processor.options}"/>
- <compilerarg value="-s"/>
- <compilerarg path="@{apgeneratedsrcdir}"/>
- <compilerarg line="${ap.proc.none.internal}"/>
- <customize/>
- </javac>
- </sequential>
- </macrodef>
- </target>
- <target depends="-init-ap-cmdline-properties" name="-init-macrodef-javac-without-processors" unless="ap.supported.internal">
- <macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3">
- <attribute default="${src.dir}:${src.data.dir}" name="srcdir"/>
- <attribute default="${build.classes.dir}" name="destdir"/>
- <attribute default="${javac.classpath}" name="classpath"/>
- <attribute default="${javac.processorpath}" name="processorpath"/>
- <attribute default="${build.generated.sources.dir}/ap-source-output" name="apgeneratedsrcdir"/>
- <attribute default="${includes}" name="includes"/>
- <attribute default="${excludes}" name="excludes"/>
- <attribute default="${javac.debug}" name="debug"/>
- <attribute default="${empty.dir}" name="sourcepath"/>
- <attribute default="${empty.dir}" name="gensrcdir"/>
- <element name="customize" optional="true"/>
- <sequential>
- <property location="${build.dir}/empty" name="empty.dir"/>
- <mkdir dir="${empty.dir}"/>
- <javac debug="@{debug}" deprecation="${javac.deprecation}" destdir="@{destdir}" encoding="${source.encoding}" excludes="@{excludes}" fork="${javac.fork}" includeantruntime="false" includes="@{includes}" source="${javac.source}" sourcepath="@{sourcepath}" srcdir="@{srcdir}" target="${javac.target}" tempdir="${java.io.tmpdir}">
- <src>
- <dirset dir="@{gensrcdir}" erroronmissingdir="false">
- <include name="*"/>
- </dirset>
- </src>
- <classpath>
- <path path="@{classpath}"/>
- </classpath>
- <compilerarg line="${endorsed.classpath.cmd.line.arg}"/>
- <compilerarg line="${javac.profile.cmd.line.arg}"/>
- <compilerarg line="${javac.compilerargs}"/>
- <customize/>
- </javac>
- </sequential>
- </macrodef>
- </target>
- <target depends="-init-macrodef-javac-with-processors,-init-macrodef-javac-without-processors" name="-init-macrodef-javac">
- <macrodef name="depend" uri="http://www.netbeans.org/ns/j2se-project/3">
- <attribute default="${src.dir}:${src.data.dir}" name="srcdir"/>
- <attribute default="${build.classes.dir}" name="destdir"/>
- <attribute default="${javac.classpath}" name="classpath"/>
- <sequential>
- <depend cache="${build.dir}/depcache" destdir="@{destdir}" excludes="${excludes}" includes="${includes}" srcdir="@{srcdir}">
- <classpath>
- <path path="@{classpath}"/>
- </classpath>
- </depend>
- </sequential>
- </macrodef>
- <macrodef name="force-recompile" uri="http://www.netbeans.org/ns/j2se-project/3">
- <attribute default="${build.classes.dir}" name="destdir"/>
- <sequential>
- <fail unless="javac.includes">Must set javac.includes</fail>
- <pathconvert pathsep="${line.separator}" property="javac.includes.binary">
- <path>
- <filelist dir="@{destdir}" files="${javac.includes}"/>
- </path>
- <globmapper from="*.java" to="*.class"/>
- </pathconvert>
- <tempfile deleteonexit="true" property="javac.includesfile.binary"/>
- <echo file="${javac.includesfile.binary}" message="${javac.includes.binary}"/>
- <delete>
- <files includesfile="${javac.includesfile.binary}"/>
- </delete>
- <delete>
- <fileset file="${javac.includesfile.binary}"/>
- </delete>
- </sequential>
- </macrodef>
- </target>
- <target if="${junit.available}" name="-init-macrodef-junit-init">
- <condition else="false" property="nb.junit.batch" value="true">
- <and>
- <istrue value="${junit.available}"/>
- <not>
- <isset property="test.method"/>
- </not>
- </and>
- </condition>
- <condition else="false" property="nb.junit.single" value="true">
- <and>
- <istrue value="${junit.available}"/>
- <isset property="test.method"/>
- </and>
- </condition>
- </target>
- <target name="-init-test-properties">
- <property name="test.binaryincludes" value="<nothing>"/>
- <property name="test.binarytestincludes" value=""/>
- <property name="test.binaryexcludes" value=""/>
- </target>
- <target if="${nb.junit.single}" name="-init-macrodef-junit-single" unless="${nb.junit.batch}">
- <macrodef name="junit" uri="http://www.netbeans.org/ns/j2se-project/3">
- <attribute default="${includes}" name="includes"/>
- <attribute default="${excludes}" name="excludes"/>
- <attribute default="**" name="testincludes"/>
- <attribute default="" name="testmethods"/>
- <element name="customize" optional="true"/>
- <sequential>
- <property name="junit.forkmode" value="perTest"/>
- <junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" forkmode="${junit.forkmode}" showoutput="true" tempdir="${build.dir}">
- <test methods="@{testmethods}" name="@{testincludes}" todir="${build.test.results.dir}"/>
- <syspropertyset>
- <propertyref prefix="test-sys-prop."/>
- <mapper from="test-sys-prop.*" to="*" type="glob"/>
- </syspropertyset>
- <formatter type="brief" usefile="false"/>
- <formatter type="xml"/>
- <jvmarg value="-ea"/>
- <customize/>
- </junit>
- </sequential>
- </macrodef>
- </target>
- <target depends="-init-test-properties" if="${nb.junit.batch}" name="-init-macrodef-junit-batch" unless="${nb.junit.single}">
- <macrodef name="junit" uri="http://www.netbeans.org/ns/j2se-project/3">
- <attribute default="${includes}" name="includes"/>
- <attribute default="${excludes}" name="excludes"/>
- <attribute default="**" name="testincludes"/>
- <attribute default="" name="testmethods"/>
- <element name="customize" optional="true"/>
- <sequential>
- <property name="junit.forkmode" value="perTest"/>
- <junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" forkmode="${junit.forkmode}" showoutput="true" tempdir="${build.dir}">
- <batchtest todir="${build.test.results.dir}">
- <fileset dir="${test.src.dir}" excludes="@{excludes},${excludes}" includes="@{includes}">
- <filename name="@{testincludes}"/>
- </fileset>
- <fileset dir="${build.test.classes.dir}" excludes="@{excludes},${excludes},${test.binaryexcludes}" includes="${test.binaryincludes}">
- <filename name="${test.binarytestincludes}"/>
- </fileset>
- </batchtest>
- <syspropertyset>
- <propertyref prefix="test-sys-prop."/>
- <mapper from="test-sys-prop.*" to="*" type="glob"/>
- </syspropertyset>
- <formatter type="brief" usefile="false"/>
- <formatter type="xml"/>
- <jvmarg value="-ea"/>
- <customize/>
- </junit>
- </sequential>
- </macrodef>
- </target>
- <target depends="-init-macrodef-junit-init,-init-macrodef-junit-single, -init-macrodef-junit-batch" if="${junit.available}" name="-init-macrodef-junit"/>
- <target if="${testng.available}" name="-init-macrodef-testng">
- <macrodef name="testng" uri="http://www.netbeans.org/ns/j2se-project/3">
- <attribute default="${includes}" name="includes"/>
- <attribute default="${excludes}" name="excludes"/>
- <attribute default="**" name="testincludes"/>
- <attribute default="" name="testmethods"/>
- <element name="customize" optional="true"/>
- <sequential>
- <condition else="" property="testng.methods.arg" value="@{testincludes}.@{testmethods}">
- <isset property="test.method"/>
- </condition>
- <union id="test.set">
- <fileset dir="${test.src.dir}" excludes="@{excludes},**/*.xml,${excludes}" includes="@{includes}">
- <filename name="@{testincludes}"/>
- </fileset>
- </union>
- <taskdef classname="org.testng.TestNGAntTask" classpath="${run.test.classpath}" name="testng"/>
- <testng classfilesetref="test.set" failureProperty="tests.failed" listeners="org.testng.reporters.VerboseReporter" methods="${testng.methods.arg}" mode="${testng.mode}" outputdir="${build.test.results.dir}" suitename="sql-dk" testname="TestNG tests" workingDir="${work.dir}">
- <xmlfileset dir="${build.test.classes.dir}" includes="@{testincludes}"/>
- <propertyset>
- <propertyref prefix="test-sys-prop."/>
- <mapper from="test-sys-prop.*" to="*" type="glob"/>
- </propertyset>
- <customize/>
- </testng>
- </sequential>
- </macrodef>
- </target>
- <target name="-init-macrodef-test-impl">
- <macrodef name="test-impl" uri="http://www.netbeans.org/ns/j2se-project/3">
- <attribute default="${includes}" name="includes"/>
- <attribute default="${excludes}" name="excludes"/>
- <attribute default="**" name="testincludes"/>
- <attribute default="" name="testmethods"/>
- <element implicit="true" name="customize" optional="true"/>
- <sequential>
- <echo>No tests executed.</echo>
- </sequential>
- </macrodef>
- </target>
- <target depends="-init-macrodef-junit" if="${junit.available}" name="-init-macrodef-junit-impl">
- <macrodef name="test-impl" uri="http://www.netbeans.org/ns/j2se-project/3">
- <attribute default="${includes}" name="includes"/>
- <attribute default="${excludes}" name="excludes"/>
- <attribute default="**" name="testincludes"/>
- <attribute default="" name="testmethods"/>
- <element implicit="true" name="customize" optional="true"/>
- <sequential>
- <j2seproject3:junit excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}">
- <customize/>
- </j2seproject3:junit>
- </sequential>
- </macrodef>
- </target>
- <target depends="-init-macrodef-testng" if="${testng.available}" name="-init-macrodef-testng-impl">
- <macrodef name="test-impl" uri="http://www.netbeans.org/ns/j2se-project/3">
- <attribute default="${includes}" name="includes"/>
- <attribute default="${excludes}" name="excludes"/>
- <attribute default="**" name="testincludes"/>
- <attribute default="" name="testmethods"/>
- <element implicit="true" name="customize" optional="true"/>
- <sequential>
- <j2seproject3:testng excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}">
- <customize/>
- </j2seproject3:testng>
- </sequential>
- </macrodef>
- </target>
- <target depends="-init-macrodef-test-impl,-init-macrodef-junit-impl,-init-macrodef-testng-impl" name="-init-macrodef-test">
- <macrodef name="test" uri="http://www.netbeans.org/ns/j2se-project/3">
- <attribute default="${includes}" name="includes"/>
- <attribute default="${excludes}" name="excludes"/>
- <attribute default="**" name="testincludes"/>
- <attribute default="" name="testmethods"/>
- <sequential>
- <j2seproject3:test-impl excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}">
- <customize>
- <classpath>
- <path path="${run.test.classpath}"/>
- </classpath>
- <jvmarg line="${endorsed.classpath.cmd.line.arg}"/>
- <jvmarg line="${run.jvmargs}"/>
- <jvmarg line="${run.jvmargs.ide}"/>
- </customize>
- </j2seproject3:test-impl>
- </sequential>
- </macrodef>
- </target>
- <target if="${junit.available}" name="-init-macrodef-junit-debug" unless="${nb.junit.batch}">
- <macrodef name="junit-debug" uri="http://www.netbeans.org/ns/j2se-project/3">
- <attribute default="${includes}" name="includes"/>
- <attribute default="${excludes}" name="excludes"/>
- <attribute default="**" name="testincludes"/>
- <attribute default="" name="testmethods"/>
- <element name="customize" optional="true"/>
- <sequential>
- <property name="junit.forkmode" value="perTest"/>
- <junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" forkmode="${junit.forkmode}" showoutput="true" tempdir="${build.dir}">
- <test methods="@{testmethods}" name="@{testincludes}" todir="${build.test.results.dir}"/>
- <syspropertyset>
- <propertyref prefix="test-sys-prop."/>
- <mapper from="test-sys-prop.*" to="*" type="glob"/>
- </syspropertyset>
- <formatter type="brief" usefile="false"/>
- <formatter type="xml"/>
- <jvmarg value="-ea"/>
- <jvmarg line="${debug-args-line}"/>
- <jvmarg value="-Xrunjdwp:transport=${debug-transport},address=${jpda.address}"/>
- <customize/>
- </junit>
- </sequential>
- </macrodef>
- </target>
- <target depends="-init-test-properties" if="${nb.junit.batch}" name="-init-macrodef-junit-debug-batch">
- <macrodef name="junit-debug" uri="http://www.netbeans.org/ns/j2se-project/3">
- <attribute default="${includes}" name="includes"/>
- <attribute default="${excludes}" name="excludes"/>
- <attribute default="**" name="testincludes"/>
- <attribute default="" name="testmethods"/>
- <element name="customize" optional="true"/>
- <sequential>
- <property name="junit.forkmode" value="perTest"/>
- <junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" forkmode="${junit.forkmode}" showoutput="true" tempdir="${build.dir}">
- <batchtest todir="${build.test.results.dir}">
- <fileset dir="${test.src.dir}" excludes="@{excludes},${excludes}" includes="@{includes}">
- <filename name="@{testincludes}"/>
- </fileset>
- <fileset dir="${build.test.classes.dir}" excludes="@{excludes},${excludes},${test.binaryexcludes}" includes="${test.binaryincludes}">
- <filename name="${test.binarytestincludes}"/>
- </fileset>
- </batchtest>
- <syspropertyset>
- <propertyref prefix="test-sys-prop."/>
- <mapper from="test-sys-prop.*" to="*" type="glob"/>
- </syspropertyset>
- <formatter type="brief" usefile="false"/>
- <formatter type="xml"/>
- <jvmarg value="-ea"/>
- <jvmarg line="${debug-args-line}"/>
- <jvmarg value="-Xrunjdwp:transport=${debug-transport},address=${jpda.address}"/>
- <customize/>
- </junit>
- </sequential>
- </macrodef>
- </target>
- <target depends="-init-macrodef-junit-debug,-init-macrodef-junit-debug-batch" if="${junit.available}" name="-init-macrodef-junit-debug-impl">
- <macrodef name="test-debug-impl" uri="http://www.netbeans.org/ns/j2se-project/3">
- <attribute default="${includes}" name="includes"/>
- <attribute default="${excludes}" name="excludes"/>
- <attribute default="**" name="testincludes"/>
- <attribute default="" name="testmethods"/>
- <element implicit="true" name="customize" optional="true"/>
- <sequential>
- <j2seproject3:junit-debug excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}">
- <customize/>
- </j2seproject3:junit-debug>
- </sequential>
- </macrodef>
- </target>
- <target if="${testng.available}" name="-init-macrodef-testng-debug">
- <macrodef name="testng-debug" uri="http://www.netbeans.org/ns/j2se-project/3">
- <attribute default="${main.class}" name="testClass"/>
- <attribute default="" name="testMethod"/>
- <element name="customize2" optional="true"/>
- <sequential>
- <condition else="-testclass @{testClass}" property="test.class.or.method" value="-methods @{testClass}.@{testMethod}">
- <isset property="test.method"/>
- </condition>
- <condition else="-suitename sql-dk -testname @{testClass} ${test.class.or.method}" property="testng.cmd.args" value="@{testClass}">
- <matches pattern=".*\.xml" string="@{testClass}"/>
- </condition>
- <delete dir="${build.test.results.dir}" quiet="true"/>
- <mkdir dir="${build.test.results.dir}"/>
- <j2seproject3:debug classname="org.testng.TestNG" classpath="${debug.test.classpath}">
- <customize>
- <customize2/>
- <jvmarg value="-ea"/>
- <arg line="${testng.debug.mode}"/>
- <arg line="-d ${build.test.results.dir}"/>
- <arg line="-listener org.testng.reporters.VerboseReporter"/>
- <arg line="${testng.cmd.args}"/>
- </customize>
- </j2seproject3:debug>
- </sequential>
- </macrodef>
- </target>
- <target depends="-init-macrodef-testng-debug" if="${testng.available}" name="-init-macrodef-testng-debug-impl">
- <macrodef name="testng-debug-impl" uri="http://www.netbeans.org/ns/j2se-project/3">
- <attribute default="${main.class}" name="testClass"/>
- <attribute default="" name="testMethod"/>
- <element implicit="true" name="customize2" optional="true"/>
- <sequential>
- <j2seproject3:testng-debug testClass="@{testClass}" testMethod="@{testMethod}">
- <customize2/>
- </j2seproject3:testng-debug>
- </sequential>
- </macrodef>
- </target>
- <target depends="-init-macrodef-junit-debug-impl" if="${junit.available}" name="-init-macrodef-test-debug-junit">
- <macrodef name="test-debug" uri="http://www.netbeans.org/ns/j2se-project/3">
- <attribute default="${includes}" name="includes"/>
- <attribute default="${excludes}" name="excludes"/>
- <attribute default="**" name="testincludes"/>
- <attribute default="" name="testmethods"/>
- <attribute default="${main.class}" name="testClass"/>
- <attribute default="" name="testMethod"/>
- <sequential>
- <j2seproject3:test-debug-impl excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}">
- <customize>
- <classpath>
- <path path="${run.test.classpath}"/>
- </classpath>
- <jvmarg line="${endorsed.classpath.cmd.line.arg}"/>
- <jvmarg line="${run.jvmargs}"/>
- <jvmarg line="${run.jvmargs.ide}"/>
- </customize>
- </j2seproject3:test-debug-impl>
- </sequential>
- </macrodef>
- </target>
- <target depends="-init-macrodef-testng-debug-impl" if="${testng.available}" name="-init-macrodef-test-debug-testng">
- <macrodef name="test-debug" uri="http://www.netbeans.org/ns/j2se-project/3">
- <attribute default="${includes}" name="includes"/>
- <attribute default="${excludes}" name="excludes"/>
- <attribute default="**" name="testincludes"/>
- <attribute default="" name="testmethods"/>
- <attribute default="${main.class}" name="testClass"/>
- <attribute default="" name="testMethod"/>
- <sequential>
- <j2seproject3:testng-debug-impl testClass="@{testClass}" testMethod="@{testMethod}">
- <customize2>
- <syspropertyset>
- <propertyref prefix="test-sys-prop."/>
- <mapper from="test-sys-prop.*" to="*" type="glob"/>
- </syspropertyset>
- </customize2>
- </j2seproject3:testng-debug-impl>
- </sequential>
- </macrodef>
- </target>
- <target depends="-init-macrodef-test-debug-junit,-init-macrodef-test-debug-testng" name="-init-macrodef-test-debug"/>
- <!--
- pre NB7.2 profiling section; consider it deprecated
- -->
- <target depends="-profile-pre-init, init, -profile-post-init, -profile-init-macrodef-profile, -profile-init-check" if="profiler.info.jvmargs.agent" name="profile-init"/>
- <target if="profiler.info.jvmargs.agent" name="-profile-pre-init">
- <!-- Empty placeholder for easier customization. -->
- <!-- You can override this target in the ../build.xml file. -->
- </target>
- <target if="profiler.info.jvmargs.agent" name="-profile-post-init">
- <!-- Empty placeholder for easier customization. -->
- <!-- You can override this target in the ../build.xml file. -->
- </target>
- <target if="profiler.info.jvmargs.agent" name="-profile-init-macrodef-profile">
- <macrodef name="resolve">
- <attribute name="name"/>
- <attribute name="value"/>
- <sequential>
- <property name="@{name}" value="${env.@{value}}"/>
- </sequential>
- </macrodef>
- <macrodef name="profile">
- <attribute default="${main.class}" name="classname"/>
- <element name="customize" optional="true"/>
- <sequential>
- <property environment="env"/>
- <resolve name="profiler.current.path" value="${profiler.info.pathvar}"/>
- <java classname="@{classname}" dir="${profiler.info.dir}" failonerror="${java.failonerror}" fork="true" jvm="${profiler.info.jvm}">
- <jvmarg line="${endorsed.classpath.cmd.line.arg}"/>
- <jvmarg value="${profiler.info.jvmargs.agent}"/>
- <jvmarg line="${profiler.info.jvmargs}"/>
- <env key="${profiler.info.pathvar}" path="${profiler.info.agentpath}:${profiler.current.path}"/>
- <arg line="${application.args}"/>
- <classpath>
- <path path="${run.classpath}"/>
- </classpath>
- <syspropertyset>
- <propertyref prefix="run-sys-prop."/>
- <mapper from="run-sys-prop.*" to="*" type="glob"/>
- </syspropertyset>
- <customize/>
- </java>
- </sequential>
- </macrodef>
- </target>
- <target depends="-profile-pre-init, init, -profile-post-init, -profile-init-macrodef-profile" if="profiler.info.jvmargs.agent" name="-profile-init-check">
- <fail unless="profiler.info.jvm">Must set JVM to use for profiling in profiler.info.jvm</fail>
- <fail unless="profiler.info.jvmargs.agent">Must set profiler agent JVM arguments in profiler.info.jvmargs.agent</fail>
- </target>
- <!--
- end of pre NB7.2 profiling section
- -->
- <target depends="-init-debug-args" name="-init-macrodef-nbjpda">
- <macrodef name="nbjpdastart" uri="http://www.netbeans.org/ns/j2se-project/1">
- <attribute default="${main.class}" name="name"/>
- <attribute default="${debug.classpath}" name="classpath"/>
- <attribute default="" name="stopclassname"/>
- <sequential>
- <nbjpdastart addressproperty="jpda.address" name="@{name}" stopclassname="@{stopclassname}" transport="${debug-transport}">
- <classpath>
- <path path="@{classpath}"/>
- </classpath>
- </nbjpdastart>
- </sequential>
- </macrodef>
- <macrodef name="nbjpdareload" uri="http://www.netbeans.org/ns/j2se-project/1">
- <attribute default="${build.classes.dir}" name="dir"/>
- <sequential>
- <nbjpdareload>
- <fileset dir="@{dir}" includes="${fix.classes}">
- <include name="${fix.includes}*.class"/>
- </fileset>
- </nbjpdareload>
- </sequential>
- </macrodef>
- </target>
- <target name="-init-debug-args">
- <property name="version-output" value="java version "${ant.java.version}"/>
- <condition property="have-jdk-older-than-1.4">
- <or>
- <contains string="${version-output}" substring="java version "1.0"/>
- <contains string="${version-output}" substring="java version "1.1"/>
- <contains string="${version-output}" substring="java version "1.2"/>
- <contains string="${version-output}" substring="java version "1.3"/>
- </or>
- </condition>
- <condition else="-Xdebug" property="debug-args-line" value="-Xdebug -Xnoagent -Djava.compiler=none">
- <istrue value="${have-jdk-older-than-1.4}"/>
- </condition>
- <condition else="dt_socket" property="debug-transport-by-os" value="dt_shmem">
- <os family="windows"/>
- </condition>
- <condition else="${debug-transport-by-os}" property="debug-transport" value="${debug.transport}">
- <isset property="debug.transport"/>
- </condition>
- </target>
- <target depends="-init-debug-args" name="-init-macrodef-debug">
- <macrodef name="debug" uri="http://www.netbeans.org/ns/j2se-project/3">
- <attribute default="${main.class}" name="classname"/>
- <attribute default="${debug.classpath}" name="classpath"/>
- <element name="customize" optional="true"/>
- <sequential>
- <java classname="@{classname}" dir="${work.dir}" failonerror="${java.failonerror}" fork="true">
- <jvmarg line="${endorsed.classpath.cmd.line.arg}"/>
- <jvmarg line="${debug-args-line}"/>
- <jvmarg value="-Xrunjdwp:transport=${debug-transport},address=${jpda.address}"/>
- <jvmarg value="-Dfile.encoding=${runtime.encoding}"/>
- <redirector errorencoding="${runtime.encoding}" inputencoding="${runtime.encoding}" outputencoding="${runtime.encoding}"/>
- <jvmarg line="${run.jvmargs}"/>
- <jvmarg line="${run.jvmargs.ide}"/>
- <classpath>
- <path path="@{classpath}"/>
- </classpath>
- <syspropertyset>
- <propertyref prefix="run-sys-prop."/>
- <mapper from="run-sys-prop.*" to="*" type="glob"/>
- </syspropertyset>
- <customize/>
- </java>
- </sequential>
- </macrodef>
- </target>
- <target name="-init-macrodef-java">
- <macrodef name="java" uri="http://www.netbeans.org/ns/j2se-project/1">
- <attribute default="${main.class}" name="classname"/>
- <attribute default="${run.classpath}" name="classpath"/>
- <attribute default="jvm" name="jvm"/>
- <element name="customize" optional="true"/>
- <sequential>
- <java classname="@{classname}" dir="${work.dir}" failonerror="${java.failonerror}" fork="true">
- <jvmarg line="${endorsed.classpath.cmd.line.arg}"/>
- <jvmarg value="-Dfile.encoding=${runtime.encoding}"/>
- <redirector errorencoding="${runtime.encoding}" inputencoding="${runtime.encoding}" outputencoding="${runtime.encoding}"/>
- <jvmarg line="${run.jvmargs}"/>
- <jvmarg line="${run.jvmargs.ide}"/>
- <classpath>
- <path path="@{classpath}"/>
- </classpath>
- <syspropertyset>
- <propertyref prefix="run-sys-prop."/>
- <mapper from="run-sys-prop.*" to="*" type="glob"/>
- </syspropertyset>
- <customize/>
- </java>
- </sequential>
- </macrodef>
- </target>
- <target name="-init-macrodef-copylibs">
- <macrodef name="copylibs" uri="http://www.netbeans.org/ns/j2se-project/3">
- <attribute default="${manifest.file}" name="manifest"/>
- <element name="customize" optional="true"/>
- <sequential>
- <property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
- <pathconvert property="run.classpath.without.build.classes.dir">
- <path path="${run.classpath}"/>
- <map from="${build.classes.dir.resolved}" to=""/>
- </pathconvert>
- <pathconvert pathsep=" " property="jar.classpath">
- <path path="${run.classpath.without.build.classes.dir}"/>
- <chainedmapper>
- <flattenmapper/>
- <filtermapper>
- <replacestring from=" " to="%20"/>
- </filtermapper>
- <globmapper from="*" to="lib/*"/>
- </chainedmapper>
- </pathconvert>
- <taskdef classname="org.netbeans.modules.java.j2seproject.copylibstask.CopyLibs" classpath="${libs.CopyLibs.classpath}" name="copylibs"/>
- <copylibs compress="${jar.compress}" excludeFromCopy="${copylibs.excludes}" index="${jar.index}" indexMetaInf="${jar.index.metainf}" jarfile="${dist.jar}" manifest="@{manifest}" manifestencoding="UTF-8" rebase="${copylibs.rebase}" runtimeclasspath="${run.classpath.without.build.classes.dir}">
- <fileset dir="${build.classes.dir}" excludes="${dist.archive.excludes}"/>
- <manifest>
- <attribute name="Class-Path" value="${jar.classpath}"/>
- <customize/>
- </manifest>
- </copylibs>
- </sequential>
- </macrodef>
- </target>
- <target name="-init-presetdef-jar">
- <presetdef name="jar" uri="http://www.netbeans.org/ns/j2se-project/1">
- <jar compress="${jar.compress}" index="${jar.index}" jarfile="${dist.jar}" manifestencoding="UTF-8">
- <j2seproject1:fileset dir="${build.classes.dir}" excludes="${dist.archive.excludes}"/>
- </jar>
- </presetdef>
- </target>
- <target name="-init-ap-cmdline-properties">
- <property name="annotation.processing.enabled" value="true"/>
- <property name="annotation.processing.processors.list" value=""/>
- <property name="annotation.processing.processor.options" value=""/>
- <property name="annotation.processing.run.all.processors" value="true"/>
- <property name="javac.processorpath" value="${javac.classpath}"/>
- <property name="javac.test.processorpath" value="${javac.test.classpath}"/>
- <condition property="ap.supported.internal" value="true">
- <not>
- <matches pattern="1\.[0-5](\..*)?" string="${javac.source}"/>
- </not>
- </condition>
- </target>
- <target depends="-init-ap-cmdline-properties" if="ap.supported.internal" name="-init-ap-cmdline-supported">
- <condition else="" property="ap.processors.internal" value="-processor ${annotation.processing.processors.list}">
- <isfalse value="${annotation.processing.run.all.processors}"/>
- </condition>
- <condition else="" property="ap.proc.none.internal" value="-proc:none">
- <isfalse value="${annotation.processing.enabled}"/>
- </condition>
- </target>
- <target depends="-init-ap-cmdline-properties,-init-ap-cmdline-supported" name="-init-ap-cmdline">
- <property name="ap.cmd.line.internal" value=""/>
- </target>
- <target depends="-pre-init,-init-private,-init-user,-init-project,-do-init,-post-init,-init-check,-init-macrodef-property,-init-macrodef-javac,-init-macrodef-test,-init-macrodef-test-debug,-init-macrodef-nbjpda,-init-macrodef-debug,-init-macrodef-java,-init-presetdef-jar,-init-ap-cmdline" name="init"/>
- <!--
- ===================
- COMPILATION SECTION
- ===================
- -->
- <target name="-deps-jar-init" unless="built-jar.properties">
- <property location="${build.dir}/built-jar.properties" name="built-jar.properties"/>
- <delete file="${built-jar.properties}" quiet="true"/>
- </target>
- <target if="already.built.jar.${basedir}" name="-warn-already-built-jar">
- <echo level="warn" message="Cycle detected: sql-dk was already built"/>
- </target>
- <target depends="init,-deps-jar-init" name="deps-jar" unless="no.deps">
- <mkdir dir="${build.dir}"/>
- <touch file="${built-jar.properties}" verbose="false"/>
- <property file="${built-jar.properties}" prefix="already.built.jar."/>
- <antcall target="-warn-already-built-jar"/>
- <propertyfile file="${built-jar.properties}">
- <entry key="${basedir}" value=""/>
- </propertyfile>
- </target>
- <target depends="init,-check-automatic-build,-clean-after-automatic-build" name="-verify-automatic-build"/>
- <target depends="init" name="-check-automatic-build">
- <available file="${build.classes.dir}/.netbeans_automatic_build" property="netbeans.automatic.build"/>
- </target>
- <target depends="init" if="netbeans.automatic.build" name="-clean-after-automatic-build">
- <antcall target="clean"/>
- </target>
- <target depends="init,deps-jar" name="-pre-pre-compile">
- <mkdir dir="${build.classes.dir}"/>
- </target>
- <target name="-pre-compile">
- <!-- Empty placeholder for easier customization. -->
- <!-- You can override this target in the ../build.xml file. -->
- </target>
- <target if="do.depend.true" name="-compile-depend">
- <pathconvert property="build.generated.subdirs">
- <dirset dir="${build.generated.sources.dir}" erroronmissingdir="false">
- <include name="*"/>
- </dirset>
- </pathconvert>
- <j2seproject3:depend srcdir="${src.dir}:${src.data.dir}:${build.generated.subdirs}"/>
- </target>
- <target depends="init,deps-jar,-pre-pre-compile,-pre-compile, -copy-persistence-xml,-compile-depend" if="have.sources" name="-do-compile">
- <j2seproject3:javac gensrcdir="${build.generated.sources.dir}"/>
- <copy todir="${build.classes.dir}">
- <fileset dir="${src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
- <fileset dir="${src.data.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
- </copy>
- </target>
- <target if="has.persistence.xml" name="-copy-persistence-xml">
- <mkdir dir="${build.classes.dir}/META-INF"/>
- <copy todir="${build.classes.dir}/META-INF">
- <fileset dir="${meta.inf.dir}" includes="persistence.xml orm.xml"/>
- </copy>
- </target>
- <target name="-post-compile">
- <!-- Empty placeholder for easier customization. -->
- <!-- You can override this target in the ../build.xml file. -->
- </target>
- <target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile,-do-compile,-post-compile" description="Compile project." name="compile"/>
- <target name="-pre-compile-single">
- <!-- Empty placeholder for easier customization. -->
- <!-- You can override this target in the ../build.xml file. -->
- </target>
- <target depends="init,deps-jar,-pre-pre-compile" name="-do-compile-single">
- <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
- <j2seproject3:force-recompile/>
- <j2seproject3:javac excludes="" gensrcdir="${build.generated.sources.dir}" includes="${javac.includes}" sourcepath="${src.dir}:${src.data.dir}"/>
- </target>
- <target name="-post-compile-single">
- <!-- Empty placeholder for easier customization. -->
- <!-- You can override this target in the ../build.xml file. -->
- </target>
- <target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile-single,-do-compile-single,-post-compile-single" name="compile-single"/>
- <!--
- ====================
- JAR BUILDING SECTION
- ====================
- -->
- <target depends="init" name="-pre-pre-jar">
- <dirname file="${dist.jar}" property="dist.jar.dir"/>
- <mkdir dir="${dist.jar.dir}"/>
- </target>
- <target name="-pre-jar">
- <!-- Empty placeholder for easier customization. -->
- <!-- You can override this target in the ../build.xml file. -->
- </target>
- <target depends="init" if="do.archive" name="-do-jar-create-manifest" unless="manifest.available">
- <tempfile deleteonexit="true" destdir="${build.dir}" property="tmp.manifest.file"/>
- <touch file="${tmp.manifest.file}" verbose="false"/>
- </target>
- <target depends="init" if="do.archive+manifest.available" name="-do-jar-copy-manifest">
- <tempfile deleteonexit="true" destdir="${build.dir}" property="tmp.manifest.file"/>
- <copy encoding="${manifest.encoding}" file="${manifest.file}" outputencoding="UTF-8" tofile="${tmp.manifest.file}"/>
- </target>
- <target depends="init,-do-jar-create-manifest,-do-jar-copy-manifest" if="do.archive+main.class.available" name="-do-jar-set-mainclass">
- <manifest encoding="UTF-8" file="${tmp.manifest.file}" mode="update">
- <attribute name="Main-Class" value="${main.class}"/>
- </manifest>
- </target>
- <target depends="init,-do-jar-create-manifest,-do-jar-copy-manifest" if="do.archive+profile.available" name="-do-jar-set-profile">
- <manifest encoding="UTF-8" file="${tmp.manifest.file}" mode="update">
- <attribute name="Profile" value="${javac.profile}"/>
- </manifest>
- </target>
- <target depends="init,-do-jar-create-manifest,-do-jar-copy-manifest" if="do.archive+splashscreen.available" name="-do-jar-set-splashscreen">
- <basename file="${application.splash}" property="splashscreen.basename"/>
- <mkdir dir="${build.classes.dir}/META-INF"/>
- <copy failonerror="false" file="${application.splash}" todir="${build.classes.dir}/META-INF"/>
- <manifest encoding="UTF-8" file="${tmp.manifest.file}" mode="update">
- <attribute name="SplashScreen-Image" value="META-INF/${splashscreen.basename}"/>
- </manifest>
- </target>
- <target depends="init,-init-macrodef-copylibs,compile,-pre-pre-jar,-pre-jar,-do-jar-create-manifest,-do-jar-copy-manifest,-do-jar-set-mainclass,-do-jar-set-profile,-do-jar-set-splashscreen" if="do.mkdist" name="-do-jar-copylibs">
- <j2seproject3:copylibs manifest="${tmp.manifest.file}"/>
- <echo level="info">To run this application from the command line without Ant, try:</echo>
- <property location="${dist.jar}" name="dist.jar.resolved"/>
- <echo level="info">java -jar "${dist.jar.resolved}"</echo>
- </target>
- <target depends="init,compile,-pre-pre-jar,-pre-jar,-do-jar-create-manifest,-do-jar-copy-manifest,-do-jar-set-mainclass,-do-jar-set-profile,-do-jar-set-splashscreen" if="do.archive" name="-do-jar-jar" unless="do.mkdist">
- <j2seproject1:jar manifest="${tmp.manifest.file}"/>
- <property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
- <property location="${dist.jar}" name="dist.jar.resolved"/>
- <pathconvert property="run.classpath.with.dist.jar">
- <path path="${run.classpath}"/>
- <map from="${build.classes.dir.resolved}" to="${dist.jar.resolved}"/>
- </pathconvert>
- <condition else="" property="jar.usage.message" value="To run this application from the command line without Ant, try:${line.separator}${platform.java} -cp ${run.classpath.with.dist.jar} ${main.class}">
- <isset property="main.class.available"/>
- </condition>
- <condition else="debug" property="jar.usage.level" value="info">
- <isset property="main.class.available"/>
- </condition>
- <echo level="${jar.usage.level}" message="${jar.usage.message}"/>
- </target>
- <target depends="-do-jar-copylibs" if="do.archive" name="-do-jar-delete-manifest">
- <delete>
- <fileset file="${tmp.manifest.file}"/>
- </delete>
- </target>
- <target depends="init,compile,-pre-pre-jar,-pre-jar,-do-jar-create-manifest,-do-jar-copy-manifest,-do-jar-set-mainclass,-do-jar-set-profile,-do-jar-set-splashscreen,-do-jar-jar,-do-jar-delete-manifest" name="-do-jar-without-libraries"/>
- <target depends="init,compile,-pre-pre-jar,-pre-jar,-do-jar-create-manifest,-do-jar-copy-manifest,-do-jar-set-mainclass,-do-jar-set-profile,-do-jar-set-splashscreen,-do-jar-copylibs,-do-jar-delete-manifest" name="-do-jar-with-libraries"/>
- <target name="-post-jar">
- <!-- Empty placeholder for easier customization. -->
- <!-- You can override this target in the ../build.xml file. -->
- </target>
- <target depends="init,compile,-pre-jar,-do-jar-without-libraries,-do-jar-with-libraries,-post-jar" name="-do-jar"/>
- <target depends="init,compile,-pre-jar,-do-jar,-post-jar" description="Build JAR." name="jar"/>
- <!--
- =================
- EXECUTION SECTION
- =================
- -->
- <target depends="init,compile" description="Run a main class." name="run">
- <j2seproject1:java>
- <customize>
- <arg line="${application.args}"/>
- </customize>
- </j2seproject1:java>
- </target>
- <target name="-do-not-recompile">
- <property name="javac.includes.binary" value=""/>
- </target>
- <target depends="init,compile-single" name="run-single">
- <fail unless="run.class">Must select one file in the IDE or set run.class</fail>
- <j2seproject1:java classname="${run.class}"/>
- </target>
- <target depends="init,compile-test-single" name="run-test-with-main">
- <fail unless="run.class">Must select one file in the IDE or set run.class</fail>
- <j2seproject1:java classname="${run.class}" classpath="${run.test.classpath}"/>
- </target>
- <!--
- =================
- DEBUGGING SECTION
- =================
- -->
- <target depends="init" if="netbeans.home" name="-debug-start-debugger">
- <j2seproject1:nbjpdastart name="${debug.class}"/>
- </target>
- <target depends="init" if="netbeans.home" name="-debug-start-debugger-main-test">
- <j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${debug.class}"/>
- </target>
- <target depends="init,compile" name="-debug-start-debuggee">
- <j2seproject3:debug>
- <customize>
- <arg line="${application.args}"/>
- </customize>
- </j2seproject3:debug>
- </target>
- <target depends="init,compile,-debug-start-debugger,-debug-start-debuggee" description="Debug project in IDE." if="netbeans.home" name="debug"/>
- <target depends="init" if="netbeans.home" name="-debug-start-debugger-stepinto">
- <j2seproject1:nbjpdastart stopclassname="${main.class}"/>
- </target>
- <target depends="init,compile,-debug-start-debugger-stepinto,-debug-start-debuggee" if="netbeans.home" name="debug-stepinto"/>
- <target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-single">
- <fail unless="debug.class">Must select one file in the IDE or set debug.class</fail>
- <j2seproject3:debug classname="${debug.class}"/>
- </target>
- <target depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-single" if="netbeans.home" name="debug-single"/>
- <target depends="init,compile-test-single" if="netbeans.home" name="-debug-start-debuggee-main-test">
- <fail unless="debug.class">Must select one file in the IDE or set debug.class</fail>
- <j2seproject3:debug classname="${debug.class}" classpath="${debug.test.classpath}"/>
- </target>
- <target depends="init,compile-test-single,-debug-start-debugger-main-test,-debug-start-debuggee-main-test" if="netbeans.home" name="debug-test-with-main"/>
- <target depends="init" name="-pre-debug-fix">
- <fail unless="fix.includes">Must set fix.includes</fail>
- <property name="javac.includes" value="${fix.includes}.java"/>
- </target>
- <target depends="init,-pre-debug-fix,compile-single" if="netbeans.home" name="-do-debug-fix">
- <j2seproject1:nbjpdareload/>
- </target>
- <target depends="init,-pre-debug-fix,-do-debug-fix" if="netbeans.home" name="debug-fix"/>
- <!--
- =================
- PROFILING SECTION
- =================
- -->
- <!--
- pre NB7.2 profiler integration
- -->
- <target depends="profile-init,compile" description="Profile a project in the IDE." if="profiler.info.jvmargs.agent" name="-profile-pre72">
- <fail unless="netbeans.home">This target only works when run from inside the NetBeans IDE.</fail>
- <nbprofiledirect>
- <classpath>
- <path path="${run.classpath}"/>
- </classpath>
- </nbprofiledirect>
- <profile/>
- </target>
- <target depends="profile-init,compile-single" description="Profile a selected class in the IDE." if="profiler.info.jvmargs.agent" name="-profile-single-pre72">
- <fail unless="profile.class">Must select one file in the IDE or set profile.class</fail>
- <fail unless="netbeans.home">This target only works when run from inside the NetBeans IDE.</fail>
- <nbprofiledirect>
- <classpath>
- <path path="${run.classpath}"/>
- </classpath>
- </nbprofiledirect>
- <profile classname="${profile.class}"/>
- </target>
- <target depends="profile-init,compile-single" if="profiler.info.jvmargs.agent" name="-profile-applet-pre72">
- <fail unless="netbeans.home">This target only works when run from inside the NetBeans IDE.</fail>
- <nbprofiledirect>
- <classpath>
- <path path="${run.classpath}"/>
- </classpath>
- </nbprofiledirect>
- <profile classname="sun.applet.AppletViewer">
- <customize>
- <arg value="${applet.url}"/>
- </customize>
- </profile>
- </target>
- <target depends="profile-init,compile-test-single" if="profiler.info.jvmargs.agent" name="-profile-test-single-pre72">
- <fail unless="netbeans.home">This target only works when run from inside the NetBeans IDE.</fail>
- <nbprofiledirect>
- <classpath>
- <path path="${run.test.classpath}"/>
- </classpath>
- </nbprofiledirect>
- <junit dir="${profiler.info.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" jvm="${profiler.info.jvm}" showoutput="true">
- <env key="${profiler.info.pathvar}" path="${profiler.info.agentpath}:${profiler.current.path}"/>
- <jvmarg value="${profiler.info.jvmargs.agent}"/>
- <jvmarg line="${profiler.info.jvmargs}"/>
- <test name="${profile.class}"/>
- <classpath>
- <path path="${run.test.classpath}"/>
- </classpath>
- <syspropertyset>
- <propertyref prefix="test-sys-prop."/>
- <mapper from="test-sys-prop.*" to="*" type="glob"/>
- </syspropertyset>
- <formatter type="brief" usefile="false"/>
- <formatter type="xml"/>
- </junit>
- </target>
- <!--
- end of pre NB72 profiling section
- -->
- <target if="netbeans.home" name="-profile-check">
- <condition property="profiler.configured">
- <or>
- <contains casesensitive="true" string="${run.jvmargs.ide}" substring="-agentpath:"/>
- <contains casesensitive="true" string="${run.jvmargs.ide}" substring="-javaagent:"/>
- </or>
- </condition>
- </target>
- <target depends="-profile-check,-profile-pre72" description="Profile a project in the IDE." if="profiler.configured" name="profile" unless="profiler.info.jvmargs.agent">
- <startprofiler/>
- <antcall target="run"/>
- </target>
- <target depends="-profile-check,-profile-single-pre72" description="Profile a selected class in the IDE." if="profiler.configured" name="profile-single" unless="profiler.info.jvmargs.agent">
- <fail unless="run.class">Must select one file in the IDE or set run.class</fail>
- <startprofiler/>
- <antcall target="run-single"/>
- </target>
- <target depends="-profile-test-single-pre72" description="Profile a selected test in the IDE." name="profile-test-single"/>
- <target depends="-profile-check" description="Profile a selected test in the IDE." if="profiler.configured" name="profile-test" unless="profiler.info.jvmargs">
- <fail unless="test.includes">Must select some files in the IDE or set test.includes</fail>
- <startprofiler/>
- <antcall target="test-single"/>
- </target>
- <target depends="-profile-check" description="Profile a selected class in the IDE." if="profiler.configured" name="profile-test-with-main">
- <fail unless="run.class">Must select one file in the IDE or set run.class</fail>
- <startprofiler/>
- <antcall target="run-test-with-main"/>
- </target>
- <target depends="-profile-check,-profile-applet-pre72" if="profiler.configured" name="profile-applet" unless="profiler.info.jvmargs.agent">
- <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
- <startprofiler/>
- <antcall target="run-applet"/>
- </target>
- <!--
- ===============
- JAVADOC SECTION
- ===============
- -->
- <target depends="init" if="have.sources" name="-javadoc-build">
- <mkdir dir="${dist.javadoc.dir}"/>
- <condition else="" property="javadoc.endorsed.classpath.cmd.line.arg" value="-J${endorsed.classpath.cmd.line.arg}">
- <and>
- <isset property="endorsed.classpath.cmd.line.arg"/>
- <not>
- <equals arg1="${endorsed.classpath.cmd.line.arg}" arg2=""/>
- </not>
- </and>
- </condition>
- <condition else="" property="bug5101868workaround" value="*.java">
- <matches pattern="1\.[56](\..*)?" string="${java.version}"/>
- </condition>
- <javadoc additionalparam="-J-Dfile.encoding=${file.encoding} ${javadoc.additionalparam}" author="${javadoc.author}" charset="UTF-8" destdir="${dist.javadoc.dir}" docencoding="UTF-8" encoding="${javadoc.encoding.used}" failonerror="true" noindex="${javadoc.noindex}" nonavbar="${javadoc.nonavbar}" notree="${javadoc.notree}" private="${javadoc.private}" source="${javac.source}" splitindex="${javadoc.splitindex}" use="${javadoc.use}" useexternalfile="true" version="${javadoc.version}" windowtitle="${javadoc.windowtitle}">
- <classpath>
- <path path="${javac.classpath}"/>
- </classpath>
- <fileset dir="${src.dir}" excludes="${bug5101868workaround},${excludes}" includes="${includes}">
- <filename name="**/*.java"/>
- </fileset>
- <fileset dir="${src.data.dir}" excludes="${bug5101868workaround},${excludes}" includes="${includes}">
- <filename name="**/*.java"/>
- </fileset>
- <fileset dir="${build.generated.sources.dir}" erroronmissingdir="false">
- <include name="**/*.java"/>
- <exclude name="*.java"/>
- </fileset>
- <arg line="${javadoc.endorsed.classpath.cmd.line.arg}"/>
- </javadoc>
- <copy todir="${dist.javadoc.dir}">
- <fileset dir="${src.dir}" excludes="${excludes}" includes="${includes}">
- <filename name="**/doc-files/**"/>
- </fileset>
- <fileset dir="${src.data.dir}" excludes="${excludes}" includes="${includes}">
- <filename name="**/doc-files/**"/>
- </fileset>
- <fileset dir="${build.generated.sources.dir}" erroronmissingdir="false">
- <include name="**/doc-files/**"/>
- </fileset>
- </copy>
- </target>
- <target depends="init,-javadoc-build" if="netbeans.home" name="-javadoc-browse" unless="no.javadoc.preview">
- <nbbrowse file="${dist.javadoc.dir}/index.html"/>
- </target>
- <target depends="init,-javadoc-build,-javadoc-browse" description="Build Javadoc." name="javadoc"/>
- <!--
- =========================
- TEST COMPILATION SECTION
- =========================
- -->
- <target depends="init,compile" if="have.tests" name="-pre-pre-compile-test">
- <mkdir dir="${build.test.classes.dir}"/>
- </target>
- <target name="-pre-compile-test">
- <!-- Empty placeholder for easier customization. -->
- <!-- You can override this target in the ../build.xml file. -->
- </target>
- <target if="do.depend.true" name="-compile-test-depend">
- <j2seproject3:depend classpath="${javac.test.classpath}" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/>
- </target>
- <target depends="init,deps-jar,compile,-pre-pre-compile-test,-pre-compile-test,-compile-test-depend" if="have.tests" name="-do-compile-test">
- <j2seproject3:javac apgeneratedsrcdir="${build.test.classes.dir}" classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" processorpath="${javac.test.processorpath}" srcdir="${test.src.dir}"/>
- <copy todir="${build.test.classes.dir}">
- <fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
- </copy>
- </target>
- <target name="-post-compile-test">
- <!-- Empty placeholder for easier customization. -->
- <!-- You can override this target in the ../build.xml file. -->
- </target>
- <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-do-compile-test,-post-compile-test" name="compile-test"/>
- <target name="-pre-compile-test-single">
- <!-- Empty placeholder for easier customization. -->
- <!-- You can override this target in the ../build.xml file. -->
- </target>
- <target depends="init,deps-jar,compile,-pre-pre-compile-test,-pre-compile-test-single" if="have.tests" name="-do-compile-test-single">
- <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
- <j2seproject3:force-recompile destdir="${build.test.classes.dir}"/>
- <j2seproject3:javac apgeneratedsrcdir="${build.test.classes.dir}" classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" excludes="" includes="${javac.includes}" processorpath="${javac.test.processorpath}" sourcepath="${test.src.dir}" srcdir="${test.src.dir}"/>
- <copy todir="${build.test.classes.dir}">
- <fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
- </copy>
- </target>
- <target name="-post-compile-test-single">
- <!-- Empty placeholder for easier customization. -->
- <!-- You can override this target in the ../build.xml file. -->
- </target>
- <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single,-do-compile-test-single,-post-compile-test-single" name="compile-test-single"/>
- <!--
- =======================
- TEST EXECUTION SECTION
- =======================
- -->
- <target depends="init" if="have.tests" name="-pre-test-run">
- <mkdir dir="${build.test.results.dir}"/>
- </target>
- <target depends="init,compile-test,-pre-test-run" if="have.tests" name="-do-test-run">
- <j2seproject3:test includes="${includes}" testincludes="**/*Test.java"/>
- </target>
- <target depends="init,compile-test,-pre-test-run,-do-test-run" if="have.tests" name="-post-test-run">
- <fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail>
- </target>
- <target depends="init" if="have.tests" name="test-report"/>
- <target depends="init" if="netbeans.home+have.tests" name="-test-browse"/>
- <target depends="init,compile-test,-pre-test-run,-do-test-run,test-report,-post-test-run,-test-browse" description="Run unit tests." name="test"/>
- <target depends="init" if="have.tests" name="-pre-test-run-single">
- <mkdir dir="${build.test.results.dir}"/>
- </target>
- <target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-do-test-run-single">
- <fail unless="test.includes">Must select some files in the IDE or set test.includes</fail>
- <j2seproject3:test excludes="" includes="${test.includes}" testincludes="${test.includes}"/>
- </target>
- <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single" if="have.tests" name="-post-test-run-single">
- <fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail>
- </target>
- <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single,-post-test-run-single" description="Run single unit test." name="test-single"/>
- <target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-do-test-run-single-method">
- <fail unless="test.class">Must select some files in the IDE or set test.class</fail>
- <fail unless="test.method">Must select some method in the IDE or set test.method</fail>
- <j2seproject3:test excludes="" includes="${javac.includes}" testincludes="${test.class}" testmethods="${test.method}"/>
- </target>
- <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single-method" if="have.tests" name="-post-test-run-single-method">
- <fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail>
- </target>
- <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single-method,-post-test-run-single-method" description="Run single unit test." name="test-single-method"/>
- <!--
- =======================
- TEST DEBUGGING SECTION
- =======================
- -->
- <target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-debug-start-debuggee-test">
- <fail unless="test.class">Must select one file in the IDE or set test.class</fail>
- <j2seproject3:test-debug excludes="" includes="${javac.includes}" testClass="${test.class}" testincludes="${javac.includes}"/>
- </target>
- <target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-debug-start-debuggee-test-method">
- <fail unless="test.class">Must select one file in the IDE or set test.class</fail>
- <fail unless="test.method">Must select some method in the IDE or set test.method</fail>
- <j2seproject3:test-debug excludes="" includes="${javac.includes}" testClass="${test.class}" testMethod="${test.method}" testincludes="${test.class}" testmethods="${test.method}"/>
- </target>
- <target depends="init,compile-test" if="netbeans.home+have.tests" name="-debug-start-debugger-test">
- <j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${test.class}"/>
- </target>
- <target depends="init,compile-test-single,-debug-start-debugger-test,-debug-start-debuggee-test" name="debug-test"/>
- <target depends="init,compile-test-single,-debug-start-debugger-test,-debug-start-debuggee-test-method" name="debug-test-method"/>
- <target depends="init,-pre-debug-fix,compile-test-single" if="netbeans.home" name="-do-debug-fix-test">
- <j2seproject1:nbjpdareload dir="${build.test.classes.dir}"/>
- </target>
- <target depends="init,-pre-debug-fix,-do-debug-fix-test" if="netbeans.home" name="debug-fix-test"/>
- <!--
- =========================
- APPLET EXECUTION SECTION
- =========================
- -->
- <target depends="init,compile-single" name="run-applet">
- <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
- <j2seproject1:java classname="sun.applet.AppletViewer">
- <customize>
- <arg value="${applet.url}"/>
- </customize>
- </j2seproject1:java>
- </target>
- <!--
- =========================
- APPLET DEBUGGING SECTION
- =========================
- -->
- <target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-applet">
- <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
- <j2seproject3:debug classname="sun.applet.AppletViewer">
- <customize>
- <arg value="${applet.url}"/>
- </customize>
- </j2seproject3:debug>
- </target>
- <target depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-applet" if="netbeans.home" name="debug-applet"/>
- <!--
- ===============
- CLEANUP SECTION
- ===============
- -->
- <target name="-deps-clean-init" unless="built-clean.properties">
- <property location="${build.dir}/built-clean.properties" name="built-clean.properties"/>
- <delete file="${built-clean.properties}" quiet="true"/>
- </target>
- <target if="already.built.clean.${basedir}" name="-warn-already-built-clean">
- <echo level="warn" message="Cycle detected: sql-dk was already built"/>
- </target>
- <target depends="init,-deps-clean-init" name="deps-clean" unless="no.deps">
- <mkdir dir="${build.dir}"/>
- <touch file="${built-clean.properties}" verbose="false"/>
- <property file="${built-clean.properties}" prefix="already.built.clean."/>
- <antcall target="-warn-already-built-clean"/>
- <propertyfile file="${built-clean.properties}">
- <entry key="${basedir}" value=""/>
- </propertyfile>
- </target>
- <target depends="init" name="-do-clean">
- <delete dir="${build.dir}"/>
- <delete dir="${dist.dir}" followsymlinks="false" includeemptydirs="true"/>
- </target>
- <target name="-post-clean">
- <!-- Empty placeholder for easier customization. -->
- <!-- You can override this target in the ../build.xml file. -->
- </target>
- <target depends="init,deps-clean,-do-clean,-post-clean" description="Clean build products." name="clean"/>
- <target name="-check-call-dep">
- <property file="${call.built.properties}" prefix="already.built."/>
- <condition property="should.call.dep">
- <and>
- <not>
- <isset property="already.built.${call.subproject}"/>
- </not>
- <available file="${call.script}"/>
- </and>
- </condition>
- </target>
- <target depends="-check-call-dep" if="should.call.dep" name="-maybe-call-dep">
- <ant antfile="${call.script}" inheritall="false" target="${call.target}">
- <propertyset>
- <propertyref prefix="transfer."/>
- <mapper from="transfer.*" to="*" type="glob"/>
- </propertyset>
- </ant>
- </target>
-</project>
--- a/java/sql-dk/nbproject/genfiles.properties Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-build.xml.data.CRC32=b51b939b
-build.xml.script.CRC32=f55b3340
-build.xml.stylesheet.CRC32=28e38971@1.56.1.46
-# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
-# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
-nbproject/build-impl.xml.data.CRC32=b51b939b
-nbproject/build-impl.xml.script.CRC32=6a0815e1
-nbproject/build-impl.xml.stylesheet.CRC32=830a3534@1.80.1.48
--- a/java/sql-dk/nbproject/project.properties Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,75 +0,0 @@
-annotation.processing.enabled=true
-annotation.processing.enabled.in.editor=false
-annotation.processing.processors.list=
-annotation.processing.run.all.processors=true
-annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output
-application.title=sql-dk
-application.vendor=fiki
-build.classes.dir=${build.dir}/classes
-build.classes.excludes=**/*.java,**/*.form
-# This directory is removed when the project is cleaned:
-build.dir=build
-build.generated.dir=${build.dir}/generated
-build.generated.sources.dir=${build.dir}/generated-sources
-# Only compile against the classpath explicitly listed here:
-build.sysclasspath=ignore
-build.test.classes.dir=${build.dir}/test/classes
-build.test.results.dir=${build.dir}/test/results
-# Uncomment to specify the preferred debugger connection transport:
-#debug.transport=dt_socket
-debug.classpath=\
- ${run.classpath}
-debug.test.classpath=\
- ${run.test.classpath}
-# This directory is removed when the project is cleaned:
-dist.dir=dist
-dist.jar=${dist.dir}/sql-dk.jar
-dist.javadoc.dir=${dist.dir}/javadoc
-endorsed.classpath=
-excludes=
-includes=**
-jar.compress=false
-javac.classpath=
-# Space-separated list of extra javac options
-javac.compilerargs=
-javac.deprecation=false
-javac.processorpath=\
- ${javac.classpath}
-javac.source=1.8
-javac.target=1.8
-javac.test.classpath=\
- ${javac.classpath}:\
- ${build.classes.dir}:\
- ${libs.testng.classpath}
-javac.test.processorpath=\
- ${javac.test.classpath}
-javadoc.additionalparam=
-javadoc.author=false
-javadoc.encoding=${source.encoding}
-javadoc.noindex=false
-javadoc.nonavbar=false
-javadoc.notree=false
-javadoc.private=false
-javadoc.splitindex=true
-javadoc.use=true
-javadoc.version=false
-javadoc.windowtitle=
-main.class=info.globalcode.sql.dk.CLIStarter
-manifest.file=manifest.mf
-meta.inf.dir=${src.dir}/META-INF
-mkdist.disabled=false
-platform.active=default_platform
-run.classpath=\
- ${javac.classpath}:\
- ${build.classes.dir}
-# Space-separated list of JVM arguments used when running the project.
-# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value.
-# To set system properties for unit tests define test-sys-prop.name=value:
-run.jvmargs=
-run.test.classpath=\
- ${javac.test.classpath}:\
- ${build.test.classes.dir}
-source.encoding=UTF-8
-src.data.dir=data
-src.dir=src
-test.src.dir=test
--- a/java/sql-dk/nbproject/project.xml Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://www.netbeans.org/ns/project/1">
- <type>org.netbeans.modules.java.j2seproject</type>
- <configuration>
- <data xmlns="http://www.netbeans.org/ns/j2se-project/3">
- <name>sql-dk</name>
- <source-roots>
- <root id="src.dir"/>
- <root id="src.data.dir"/>
- </source-roots>
- <test-roots>
- <root id="test.src.dir"/>
- </test-roots>
- </data>
- </configuration>
-</project>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/pom.xml Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>info.globalcode.sql.dk</groupId>
+ <artifactId>sql-dk</artifactId>
+ <version>0.10-SNAPSHOT</version>
+ <packaging>jar</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.testng</groupId>
+ <artifactId>testng</artifactId>
+ <version>6.9.9</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <maven.compiler.source>1.8</maven.compiler.source>
+ <maven.compiler.target>1.8</maven.compiler.target>
+ </properties>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <version>1.6.0</version>
+ <executions>
+ <execution>
+ <id>version-info</id>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>exec</goal>
+ </goals>
+ <configuration>
+ <executable>./version-info.sh</executable>
+ <outputFile>src/main/resources/info/globalcode/sql/dk/version.txt</outputFile><!-- TODO: move to target/generated-sources/ -->
+ </configuration>
+ </execution>
+ <execution>
+ <id>help-generator</id>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>exec</goal>
+ </goals>
+ <configuration>
+ <executable>./help-generator.sh</executable>
+ <outputFile>src/main/resources/info/globalcode/sql/dk/help.txt</outputFile><!-- TODO: move to target/generated-sources/ -->
+ </configuration>
+ </execution>
+ <execution>
+ <id>bash-completion</id>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>exec</goal>
+ </goals>
+ <configuration>
+ <executable>./bash-completion.sh</executable>
+ <outputFile>target/bash-completion.sh</outputFile>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
--- a/java/sql-dk/src/info/globalcode/sql/dk/CLIOptions.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,283 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk;
-
-import static info.globalcode.sql.dk.Functions.isNotEmpty;
-import static info.globalcode.sql.dk.Functions.equalz;
-import info.globalcode.sql.dk.InfoLister.InfoType;
-import info.globalcode.sql.dk.configuration.Properties;
-import info.globalcode.sql.dk.configuration.Property;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.EnumSet;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.regex.Pattern;
-import java.util.regex.PatternSyntaxException;
-
-/**
- * Holds options from command line, validates them, combines with configuration and provides derived
- * objects.
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class CLIOptions {
-
- public static final String DEFAULT_NAME_PREFIX = ":";
- public static final String DEFAULT_NAME_SUFFIX = "(?=([^\\w]|$))";
- private String sql;
- private String databaseName;
- private final Set<String> databaseNamesToTest = new LinkedHashSet<>();
- private final Set<String> databaseNamesToListProperties = new LinkedHashSet<>();
- private final Set<String> formatterNamesToListProperties = new LinkedHashSet<>();
- private String namePrefix = DEFAULT_NAME_PREFIX;
- private String nameSuffix = DEFAULT_NAME_SUFFIX;
- private String formatterName;
- private boolean batch;
- private final Properties formatterProperties = new Properties();
- private final Properties databaseProperties = new Properties();
-
- public enum MODE {
-
- QUERY_NOW,
- PREPARE_BATCH,
- EXECUTE_BATCH,
- JUST_SHOW_INFO
- }
- private final List<NamedParameter> namedParameters = new ArrayList<>();
- private final List<Parameter> numberedParameters = new ArrayList<>();
- private final EnumSet<InfoType> showInfo = EnumSet.noneOf(InfoType.class);
-
- public void validate() throws InvalidOptionsException {
- InvalidOptionsException e = new InvalidOptionsException();
-
- MODE mode = getMode();
- if (mode == null) {
- e.addProblem(new InvalidOptionsException.OptionProblem("Invalid combination of DB, SQL and BATCH – please specify just 2 of this 3 options"));
- } else if (mode == MODE.JUST_SHOW_INFO) {
- if (!namedParameters.isEmpty()) {
- e.addProblem(new InvalidOptionsException.OptionProblem("Do not use named parameters if just showing info."));
- }
- if (!numberedParameters.isEmpty()) {
- e.addProblem(new InvalidOptionsException.OptionProblem("Do not use numbered parameters if just showing info."));
- }
- if (isNotEmpty(sql, false)) {
- e.addProblem(new InvalidOptionsException.OptionProblem("Do not specify SQL if just showing info."));
- }
- if (isNotEmpty(databaseName, false)) {
- e.addProblem(new InvalidOptionsException.OptionProblem("Do not specify database if just showing info."));
- }
- if (batch) {
- e.addProblem(new InvalidOptionsException.OptionProblem("Do not specify batch if just showing info."));
- }
- if (!equalz(namePrefix, DEFAULT_NAME_PREFIX)) {
- e.addProblem(new InvalidOptionsException.OptionProblem("Do not specify name prefix if just showing info."));
- }
- if (!equalz(nameSuffix, DEFAULT_NAME_SUFFIX)) {
- e.addProblem(new InvalidOptionsException.OptionProblem("Do not specify name suffix if just showing info."));
- }
- if (showInfo.contains(InfoType.CONNECTION) && databaseNamesToTest.isEmpty()) {
- e.addProblem(new InvalidOptionsException.OptionProblem("Please specify which database should be tested."));
- }
- if (showInfo.contains(InfoType.JDBC_PROPERTIES) && databaseNamesToListProperties.isEmpty()) {
- e.addProblem(new InvalidOptionsException.OptionProblem("Please specify for which database the properties should be listed."));
- }
- }
-
- if (!namedParameters.isEmpty() && !numberedParameters.isEmpty()) {
- e.addProblem(new InvalidOptionsException.OptionProblem("Named and numbered parameters can not be used together in one command."));
- }
-
- try {
- Pattern.compile(namePrefix + "test" + nameSuffix);
- } catch (PatternSyntaxException regexException) {
- e.addProblem(new InvalidOptionsException.OptionProblem("Ivalid regular expression in name prefix or suffix", regexException));
- }
-
- if (e.hasProblems()) {
- throw e;
- }
- }
-
- private boolean hasSql() {
- return isNotEmpty(getSql(), true);
- }
-
- private boolean hasDb() {
- return isNotEmpty(getDatabaseName(), true);
- }
-
- /**
- * Depends on options: DB, BATCH, SQL
- *
- * @return mode | or null if options are not yet initialized or combination of options is
- * invalid
- */
- public MODE getMode() {
- if (hasDb() && !batch && hasSql()) {
- return MODE.QUERY_NOW;
- } else if (!hasDb() && batch && hasSql()) {
- return MODE.PREPARE_BATCH;
- } else if (hasDb() && batch && !hasSql()) {
- return MODE.EXECUTE_BATCH;
- } else {
- return showInfo.isEmpty() ? null : MODE.JUST_SHOW_INFO;
- }
- }
-
- public String getSql() {
- return sql;
- }
-
- public void setSql(String sql) {
- this.sql = sql;
- }
-
- public String getDatabaseName() {
- return databaseName;
- }
-
- public void setDatabaseName(String databaseName) {
- this.databaseName = databaseName;
- }
-
- public void setBatch(boolean batch) {
- this.batch = batch;
- }
-
- public Collection<NamedParameter> getNamedParameters() {
- return namedParameters;
- }
-
- public List<Parameter> getNumberedParameters() {
- return numberedParameters;
- }
-
- public void addNumberedParameter(Parameter p) {
- numberedParameters.add(p);
- }
-
- public void addNamedParameter(NamedParameter p) {
- namedParameters.add(p);
- }
-
- public Properties getDatabaseProperties() {
- return databaseProperties;
- }
-
- public Properties getFormatterProperties() {
- return formatterProperties;
- }
-
- public void addDatabaseProperty(Property p) {
- databaseProperties.add(p);
- }
-
- public void addFormatterProperty(Property p) {
- formatterProperties.add(p);
- }
-
- /**
- * @return regular expression describing the name prefix
- */
- public String getNamePrefix() {
- return namePrefix;
- }
-
- /**
- * @param namePrefix
- * @see #getNamePrefix()
- */
- public void setNamePrefix(String namePrefix) {
- this.namePrefix = namePrefix;
- }
-
- /**
- * @return regular expression describing the name prefix
- */
- public String getNameSuffix() {
- return nameSuffix;
- }
-
- /**
- * @param nameSuffix
- * @see #getNameSuffix()
- */
- public void setNameSuffix(String nameSuffix) {
- this.nameSuffix = nameSuffix;
- }
-
- public String getFormatterName() {
- return formatterName;
- }
-
- public void setFormatterName(String formatterName) {
- this.formatterName = formatterName;
- }
-
- public void addShowInfo(InfoType info) {
- showInfo.add(info);
- }
-
- public EnumSet<InfoType> getShowInfo() {
- return showInfo;
- }
-
- public Set<String> getDatabaseNamesToTest() {
- return databaseNamesToTest;
- }
-
- public void addDatabaseNameToTest(String name) {
- databaseNamesToTest.add(name);
- }
-
- public Set<String> getDatabaseNamesToListProperties() {
- return databaseNamesToListProperties;
- }
-
- public void addDatabaseNameToListProperties(String name) {
- databaseNamesToListProperties.add(name);
- }
-
- public Set<String> getFormatterNamesToListProperties() {
- return formatterNamesToListProperties;
- }
-
- public void addFormatterNameToListProperties(String name) {
- formatterNamesToListProperties.add(name);
- }
-
- public SQLCommand getSQLCommand() {
- if (namedParameters.isEmpty()) {
- return new SQLCommandNumbered(sql, numberedParameters);
- } else {
- return new SQLCommandNamed(sql, namedParameters, namePrefix, nameSuffix);
- }
- }
-
- public OutputStream getOutputStream() {
- return System.out;
- }
-
- public InputStream getInputStream() {
- return System.in;
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/CLIParser.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,216 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk;
-
-import static info.globalcode.sql.dk.Functions.readString;
-import info.globalcode.sql.dk.InfoLister.InfoType;
-import info.globalcode.sql.dk.configuration.Property;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Converts command line arguments from String array to object.
- * Checks basic constraints (if only supported options are used and if they have correct number of
- * parameters)
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class CLIParser {
-
- public static final String TYPE_NAME_SEPARATOR = ":";
-
- public CLIOptions parseOptions(String[] args, InputStream in) throws CLIParserException {
- CLIOptions options = new CLIOptions();
-
- List<SQLType> numberedTypes = new ArrayList<>();
- Map<String, SQLType> namedTypes = new HashMap<>();
-
- for (int i = 0; i < args.length; i++) {
- String arg = args[i];
- switch (arg) {
- case Tokens.TYPES:
- String typesString = fetchNext(args, ++i);
-
- for (String oneType : typesString.split(",")) {
- int sepatratorIndex = oneType.indexOf(TYPE_NAME_SEPARATOR);
- if (sepatratorIndex == -1) {
- numberedTypes.add(getType(oneType.toUpperCase()));
- } else {
- String namePart = oneType.substring(0, sepatratorIndex).trim();
- String typePart = oneType.substring(sepatratorIndex + TYPE_NAME_SEPARATOR.length(), oneType.length());
- namedTypes.put(namePart, getType(typePart.toUpperCase()));
- }
- }
- break;
- case Tokens.NAME_PREFIX:
- options.setNamePrefix(fetchNext(args, ++i));
- break;
- case Tokens.NAME_SUFFIX:
- options.setNameSuffix(fetchNext(args, ++i));
- break;
- case Tokens.DB:
- options.setDatabaseName(fetchNext(args, ++i));
- break;
- case Tokens.SQL:
- options.setSql(fetchNext(args, ++i));
- break;
- case Tokens.SQL_IN:
- try {
- options.setSql(readString(in));
- } catch (IOException e) {
- throw new CLIParserException("Unable to read SQL from the input stream", e);
- }
- break;
- case Tokens.BATCH:
- options.setBatch(true);
- break;
- case Tokens.DATA: // --data is the last option
- for (i++; i < args.length; i++) {
- arg = args[i];
- Parameter parameter;
- if (numberedTypes.isEmpty()) {
- parameter = new Parameter(arg, null);
- } else {
- int paramIndex = options.getNumberedParameters().size();
- SQLType paramType;
- try {
- paramType = numberedTypes.get(paramIndex);
- } catch (IndexOutOfBoundsException e) {
- throw new CLIParserException("Missing type for parameter #" + paramIndex, e);
- } catch (NullPointerException e) {
- throw new CLIParserException("Invalid type definition for parameter #" + paramIndex, e);
- }
- parameter = new Parameter(arg, paramType);
- }
- options.addNumberedParameter(parameter);
- }
- break;
- case Tokens.DATA_NAMED:
- for (i++; i < args.length; i++) {
- String paramName = args[i];
- String paramValue = fetchNext(args, ++i);
- options.addNamedParameter(new NamedParameter(paramName, paramValue, namedTypes.get(paramName)));
- }
- break;
- case Tokens.FORMATTER:
- options.setFormatterName(fetchNext(args, ++i));
- break;
- case Tokens.DB_PROPERTY:
- options.addDatabaseProperty(new Property(fetchNext(args, ++i), fetchNext(args, ++i)));
- break;
- case Tokens.FORMATTER_PROPERTY:
- options.addFormatterProperty(new Property(fetchNext(args, ++i), fetchNext(args, ++i)));
- break;
- case Tokens.INFO_HELP:
- options.addShowInfo(InfoType.HELP);
- break;
- case Tokens.INFO_FORMATTERS:
- options.addShowInfo(InfoType.FORMATTERS);
- break;
- case Tokens.INFO_FORMATTER_PROPERTIES:
- options.addShowInfo(InfoType.FORMATTER_PROPERTIES);
- options.addFormatterNameToListProperties(fetchNext(args, ++i));
- break;
- case Tokens.INFO_LICENSE:
- options.addShowInfo(InfoType.LICENSE);
- break;
- case Tokens.INFO_JAVA_PROPERTIES:
- options.addShowInfo(InfoType.JAVA_PROPERTIES);
- break;
- case Tokens.INFO_ENVIRONMENT_VARIABLES:
- options.addShowInfo(InfoType.ENVIRONMENT_VARIABLES);
- break;
- case Tokens.INFO_TYPES:
- options.addShowInfo(InfoType.TYPES);
- break;
- case Tokens.INFO_VERSION:
- options.addShowInfo(InfoType.VERSION);
- break;
- case Tokens.INFO_JDBC_DRIVERS:
- options.addShowInfo(InfoType.JDBC_DRIVERS);
- break;
- case Tokens.INFO_JDBC_PROPERTIES:
- options.addShowInfo(InfoType.JDBC_PROPERTIES);
- options.addDatabaseNameToListProperties(fetchNext(args, ++i));
- break;
- case Tokens.INFO_DATABASES:
- options.addShowInfo(InfoType.DATABASES);
- break;
- case Tokens.INFO_CONNECTION:
- options.addShowInfo(InfoType.CONNECTION);
- options.addDatabaseNameToTest(fetchNext(args, ++i));
- break;
- default:
- throw new CLIParserException("Unknown option: " + arg);
- }
- }
- return options;
- }
-
- private String fetchNext(String[] args, int index) throws CLIParserException {
- if (index < args.length) {
- return args[index];
- } else {
- throw new CLIParserException("Expecting value for option: " + args[index - 1]);
- }
- }
-
- public static class Tokens {
-
- // bash-completion:options:
- public static final String DB = "--db"; // bash-completion:option // help: database name
- public static final String DB_PROPERTY = "--db-property"; // bash-completion:option // help: name and value
- public static final String SQL = "--sql"; // bash-completion:option // help: SQL query/command
- public static final String SQL_IN = "--sql-in"; // bash-completion:option // help: SQL query/command
- public static final String BATCH = "--batch"; // bash-completion:option // help: batch mode (no argument)
- public static final String DATA = "--data"; // bash-completion:option // help: list of ordinal parameters
- public static final String DATA_NAMED = "--data-named"; // bash-completion:option // help: list of named parameters
- public static final String NAME_PREFIX = "--name-prefix"; // bash-completion:option // help: parameter name prefix – regular expression
- public static final String NAME_SUFFIX = "--name-suffix"; // bash-completion:option // help: parameter name suffix – regular expression
- public static final String TYPES = "--types"; // bash-completion:option // help: comma separated list of parameter types
- public static final String FORMATTER = "--formatter"; // bash-completion:option // help: name of the output formatter
- public static final String FORMATTER_PROPERTY = "--formatter-property"; // bash-completion:option // help: name and value
- public static final String INFO_HELP = "--help"; // bash-completion:option // help: print this help
- public static final String INFO_VERSION = "--version"; // bash-completion:option // help: print version info
- public static final String INFO_LICENSE = "--license"; // bash-completion:option // help: print license
- public static final String INFO_JAVA_PROPERTIES = "--list-java-properties"; // bash-completion:option // help: list of Java system properties
- public static final String INFO_ENVIRONMENT_VARIABLES = "--list-environment-variables"; // bash-completion:option // help: list of environment variables
- public static final String INFO_FORMATTERS = "--list-formatters"; // bash-completion:option // help: print list of available formatters
- public static final String INFO_FORMATTER_PROPERTIES = "--list-formatter-properties"; // bash-completion:option // help: print list of available properties for given formatter
- public static final String INFO_TYPES = "--list-types"; // bash-completion:option // help: print list of available data types
- public static final String INFO_JDBC_DRIVERS = "--list-jdbc-drivers"; // bash-completion:option // help: list of available JDBC drivers
- public static final String INFO_JDBC_PROPERTIES = "--list-jdbc-properties"; // bash-completion:option // help: list of available JDBC properties for given database
- public static final String INFO_DATABASES = "--list-databases"; // bash-completion:option // help: print list of configured databases
- public static final String INFO_CONNECTION = "--test-connection"; // bash-completion:option // help: test connection to particular database
-
- private Tokens() {
- }
- }
-
- private SQLType getType(String typeString) throws CLIParserException {
- try {
- return SQLType.valueOf(typeString.trim());
- } catch (IllegalArgumentException e) {
- throw new CLIParserException("Unsupported type: " + typeString, e);
- }
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/CLIParserException.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk;
-
-/**
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class CLIParserException extends DKException {
-
- public CLIParserException() {
- }
-
- public CLIParserException(String message) {
- super(message);
- }
-
- public CLIParserException(Throwable cause) {
- super(cause);
- }
-
- public CLIParserException(String message, Throwable cause) {
- super(message, cause);
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/CLIStarter.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,274 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk;
-
-import info.globalcode.sql.dk.configuration.ConfigurationProvider;
-import info.globalcode.sql.dk.CLIOptions.MODE;
-import info.globalcode.sql.dk.batch.Batch;
-import info.globalcode.sql.dk.batch.BatchDecoder;
-import info.globalcode.sql.dk.batch.BatchException;
-import info.globalcode.sql.dk.batch.BatchEncoder;
-import info.globalcode.sql.dk.configuration.Configuration;
-import info.globalcode.sql.dk.configuration.ConfigurationException;
-import info.globalcode.sql.dk.configuration.DatabaseDefinition;
-import info.globalcode.sql.dk.configuration.FormatterDefinition;
-import info.globalcode.sql.dk.configuration.Loader;
-import info.globalcode.sql.dk.configuration.NameIdentified;
-import info.globalcode.sql.dk.configuration.PropertyDeclaration;
-import info.globalcode.sql.dk.formatting.Formatter;
-import info.globalcode.sql.dk.formatting.FormatterContext;
-import info.globalcode.sql.dk.formatting.FormatterException;
-import info.globalcode.sql.dk.jmx.ConnectionManagement;
-import info.globalcode.sql.dk.jmx.ManagementUtils;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.io.PrintWriter;
-import java.sql.SQLException;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.logging.Level;
-import java.util.logging.LogRecord;
-import java.util.logging.Logger;
-
-/**
- * Entry point of the command line interface of SQL-DK.
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class CLIStarter implements ConfigurationProvider {
-
- // help:exit-codes
- public static final int EXIT_SUCCESS = 0; // doc:success
- public static final int EXIT_UNEXPECTED_ERROR = 1; // doc:unexpected error (probably bug)
- // 2 is reserved: http://www.tldp.org/LDP/abs/html/exitcodes.html#EXITCODESREF
- public static final int EXIT_SQL_ERROR = 3; // doc:SQL error
- public static final int EXIT_CLI_PARSE_ERROR = 4; // doc:CLI options parse error
- public static final int EXIT_CLI_VALIDATE_ERROR = 5; // doc:CLI options validation error
- public static final int EXIT_CONFIGURATION_ERROR = 6; // doc:configuration error
- public static final int EXIT_FORMATTING_ERROR = 7; // doc:formatting error
- public static final int EXIT_BATCH_ERROR = 8; // doc:batch error
- private static final Logger log = Logger.getLogger(CLIStarter.class.getName());
- private final CLIOptions options;
- private final Loader configurationLoader = new Loader();
- private Configuration configuration;
-
- public static void main(String[] args) {
- log.log(Level.FINE, "Starting " + Constants.PROGRAM_NAME);
- int exitCode;
-
- if (args.length == 0) {
- args = new String[]{CLIParser.Tokens.INFO_HELP};
- }
-
- try {
- CLIParser parser = new CLIParser();
- CLIOptions options = parser.parseOptions(args, System.in);
- options.validate();
- CLIStarter starter = new CLIStarter(options);
- starter.installDefaultConfiguration();
- starter.process();
- log.log(Level.FINE, "All done");
- exitCode = EXIT_SUCCESS;
- } catch (CLIParserException e) {
- log.log(Level.SEVERE, "Unable to parse CLI options", e);
- exitCode = EXIT_CLI_PARSE_ERROR;
- } catch (InvalidOptionsException e) {
- log.log(Level.SEVERE, "Invalid CLI options", e);
- for (InvalidOptionsException.OptionProblem p : e.getProblems()) {
- LogRecord r = new LogRecord(Level.SEVERE, "Option problem: {0}");
- r.setThrown(p.getException());
- r.setParameters(new Object[]{p.getDescription()});
- log.log(r);
- }
- exitCode = EXIT_CLI_VALIDATE_ERROR;
- } catch (ConfigurationException e) {
- log.log(Level.SEVERE, "Configuration problem", e);
- exitCode = EXIT_CONFIGURATION_ERROR;
- } catch (SQLException e) {
- log.log(Level.SEVERE, "SQL problem", e);
- exitCode = EXIT_SQL_ERROR;
- } catch (FormatterException e) {
- log.log(Level.SEVERE, "Formatting problem", e);
- exitCode = EXIT_FORMATTING_ERROR;
- } catch (BatchException e) {
- log.log(Level.SEVERE, "Batch problem", e);
- exitCode = EXIT_BATCH_ERROR;
- }
-
- System.exit(exitCode);
- }
-
- public CLIStarter(CLIOptions options) {
- this.options = options;
- }
-
- private void process() throws ConfigurationException, SQLException, FormatterException, BatchException {
- MODE mode = options.getMode();
-
- /** Show info */
- if (!options.getShowInfo().isEmpty()) {
- PrintStream infoOut = mode == MODE.JUST_SHOW_INFO ? System.out : System.err;
- InfoLister infoLister = new InfoLister(infoOut, this, options);
- infoLister.showInfo();
- }
-
- switch (mode) {
- case QUERY_NOW:
- processQueryNow();
- break;
- case PREPARE_BATCH:
- processPrepareBatch();
- break;
- case EXECUTE_BATCH:
- processExecuteBatch();
- break;
- case JUST_SHOW_INFO:
- // already done above
- break;
- default:
- log.log(Level.SEVERE, "Unsupported mode: {0}", mode);
- break;
- }
-
- generateBashCompletion();
- }
-
- private void processQueryNow() throws ConfigurationException, SQLException, FormatterException {
- DatabaseDefinition dd = getConfiguration().getDatabase(options.getDatabaseName());
- FormatterDefinition fd = configuration.getFormatter(options.getFormatterName());
- ConnectionManagement jmxBean = ManagementUtils.registerMBean(dd.getName());
-
- try (DatabaseConnection c = dd.connect(options.getDatabaseProperties(), jmxBean)) {
- log.log(Level.FINE, "Database connected");
- try (Formatter f = fd.getInstance(new FormatterContext(options.getOutputStream(), options.getFormatterProperties()))) {
- c.executeQuery(options.getSQLCommand(), f);
- }
- }
- }
-
- private void processPrepareBatch() throws BatchException {
- BatchEncoder enc = new BatchEncoder();
- int length = enc.encode(options.getSQLCommand(), options.getOutputStream());
- log.log(Level.FINE, "Prepared batch size: {0} bytes", length);
- }
-
- private void processExecuteBatch() throws ConfigurationException, SQLException, FormatterException, BatchException {
- BatchDecoder dec = new BatchDecoder();
- Batch b = dec.decode(options.getInputStream());
-
- DatabaseDefinition dd = getConfiguration().getDatabase(options.getDatabaseName());
- FormatterDefinition fd = configuration.getFormatter(options.getFormatterName());
- ConnectionManagement jmxBean = ManagementUtils.registerMBean(dd.getName());
-
- try (DatabaseConnection c = dd.connect(options.getDatabaseProperties(), jmxBean)) {
- log.log(Level.FINE, "Database connected");
- try (Formatter f = fd.getInstance(new FormatterContext(options.getOutputStream(), options.getFormatterProperties()))) {
- c.executeBatch(b, f);
- }
- }
- }
-
- @Override
- public Configuration getConfiguration() throws ConfigurationException {
- if (configuration == null) {
- configuration = configurationLoader.loadConfiguration();
- }
- return configuration;
- }
-
- private void installDefaultConfiguration() throws ConfigurationException {
- Constants.DIR.mkdir();
-
- if (Constants.CONFIG_FILE.exists()) {
- log.log(Level.FINER, "Config file already exists: {0}", Constants.CONFIG_FILE);
- } else {
- try {
- Functions.installResource(Constants.EXAMPLE_CONFIG_FILE, Constants.CONFIG_FILE);
- log.log(Level.FINE, "Installing default config file: {0}", Constants.CONFIG_FILE);
- } catch (IOException e) {
- throw new ConfigurationException("Unable to write example configuration to " + Constants.CONFIG_FILE, e);
- }
- }
- }
-
- private void generateBashCompletion() {
- if (configuration == null) {
- log.log(Level.FINER, "Not writing Bash completion helper files. In order to generate these files please run some command which requires configuration.");
- } else {
- try {
- File dir = new File(Constants.DIR, "bash-completion");
- dir.mkdir();
- writeBashCompletionHelperFile(configuration.getDatabases(), new File(dir, "databases"));
- writeBashCompletionHelperFile(configuration.getAllFormatters(), new File(dir, "formatters"));
- writeBashCompletionHelperFileForFormatterProperties(new File(dir, "formatter-properties"));
- } catch (Exception e) {
- log.log(Level.WARNING, "Unable to generate Bash completion helper files", e);
- }
- }
- }
-
- private void writeBashCompletionHelperFile(Collection<? extends NameIdentified> items, File target) throws FileNotFoundException {
- if (Constants.CONFIG_FILE.lastModified() > target.lastModified()) {
- try (PrintWriter fw = new PrintWriter(target)) {
- for (NameIdentified dd : items) {
- fw.println(dd.getName());
- }
- fw.close();
- log.log(Level.FINE, "Bash completion helper file was written: {0}", target);
- }
- } else {
- log.log(Level.FINER, "Not writing Bash completion helper file: {0} because configuration {1} has not been changed", new Object[]{target, Constants.CONFIG_FILE});
- }
- }
-
- private void writeBashCompletionHelperFileForFormatterProperties(File formattersDir) throws ClassNotFoundException, FileNotFoundException {
- if (Constants.CONFIG_FILE.lastModified() > formattersDir.lastModified()) {
- // TODO: delete old directory
- formattersDir.mkdir();
- for (FormatterDefinition fd : configuration.getAllFormatters()) {
- File formatterDir = new File(formattersDir, fd.getName());
- formatterDir.mkdir();
-
- Class<Formatter> formatterClass = (Class<Formatter>) Class.forName(fd.getClassName());
- List<Class<? extends Formatter>> hierarchy = Functions.getClassHierarchy(formatterClass, Formatter.class);
- Collections.reverse(hierarchy);
- for (Class<? extends Formatter> c : hierarchy) {
- for (PropertyDeclaration p : Functions.getPropertyDeclarations(c)) {
- File propertyDir = new File(formatterDir, p.name());
- propertyDir.mkdir();
- File choicesFile = new File(propertyDir, "choices");
- try (PrintWriter fw = new PrintWriter(choicesFile)) {
- // TODO: refactor, move
- if (p.type() == Boolean.class) {
- fw.println("true");
- fw.println("false");
- }
- }
- }
- }
- }
- log.log(Level.FINE, "Bash completion helper files was written in: {0}", formattersDir);
- } else {
- log.log(Level.FINER, "Not writing Bash completion helper directory: {0} because configuration {1} has not been changed", new Object[]{formattersDir, Constants.CONFIG_FILE});
- }
-
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/ColorfulPrintWriter.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,358 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.OutputStream;
-import java.io.PrintWriter;
-import java.io.UnsupportedEncodingException;
-import java.io.Writer;
-import java.util.EnumSet;
-
-/**
- * PrintWriter with convenience methods for printing color and formatted text.
- *
- * Uses ANSI Escape Sequences.
- * See: http://www.tldp.org/HOWTO/Bash-Prompt-HOWTO/x329.html
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class ColorfulPrintWriter extends PrintWriter {
-
- public enum TerminalColor {
-
- Black(30, 40),
- Red(31, 41),
- Green(32, 42),
- Yellow(33, 43),
- Blue(34, 44),
- Magenta(35, 45),
- Cyan(36, 46),
- White(37, 47);
- private final int foregroundCode;
- private final int backgroundCode;
-
- private TerminalColor(int foregroundCode, int backgroundCode) {
- this.foregroundCode = foregroundCode;
- this.backgroundCode = backgroundCode;
- }
-
- public int getForegroundCode() {
- return foregroundCode;
- }
-
- public int getBackgroundCode() {
- return backgroundCode;
- }
- }
-
- public enum TerminalStyle {
-
- Reset(0),
- Bright(1),
- Dim(2),
- Underscore(4),
- Blink(5),
- Reverse(7),
- Hidden(8);
- private int code;
-
- private TerminalStyle(int code) {
- this.code = code;
- }
-
- public int getCode() {
- return code;
- }
- }
- private final boolean COLOR_ENABLED;
- private boolean colorful = true;
-
- public void setStyle(TerminalStyle style) {
- setStyle(EnumSet.of(style));
- }
-
- public void setStyle(EnumSet<TerminalStyle> styles) {
- printCodes(getStyleCodes(styles));
- }
-
- private static int[] getStyleCodes(EnumSet<TerminalStyle> styles) {
- int[] array = new int[styles.size()];
- int i = 0;
- for (TerminalStyle s : styles) {
- array[i++] = s.getCode();
- }
- return array;
- }
-
- /**
- * Print (usually audible) bell code (\007, \a, ^G)
- */
- public void bell() {
- print("\007");
- }
-
- /**
- * Eat the last character
- */
- public void backspace() {
- print("\b");
- }
-
- /**
- * Eat n last characters
- *
- * @param count n
- */
- public void backspace(int count) {
- for (int i = 0; i < count; i++) {
- backspace();
- }
- }
-
- /**
- * With 100 ms delay and all colors.
- *
- * @see #printRainbow(java.lang.String, int,
- * info.globalcode.sql.dk.ColorfulPrintWriter.TerminalColor[])
- */
- public void printRainbow(String string) {
- printRainbow(string, 100);
- }
-
- /**
- * With all colors.
- *
- * @see #printRainbow(java.lang.String, int,
- * info.globalcode.sql.dk.ColorfulPrintWriter.TerminalColor[])
- */
- public void printRainbow(String string, int delay) {
- printRainbow(string, delay, TerminalColor.values());
- }
-
- /**
- * Prints rainbow text – (re)writes same text subsequently in given colors and then in default
- * color.
- *
- * @param string text to be printed, should not contain \n new line (then rainbow does not work
- * – use println() after printRainbow() instead)
- * @param delay delay between rewrites
- * @param colors list of colors to be used
- */
- public void printRainbow(String string, int delay, TerminalColor... colors) {
- for (TerminalColor c : colors) {
- print(c, string);
- try {
- Thread.sleep(delay);
- } catch (InterruptedException e) {
- // no time to sleep
- break;
- }
- backspace(string.length());
- flush();
- }
- print(string);
- }
-
- public void setForegroundColor(TerminalColor color) {
- printCodes(color.getForegroundCode());
- }
-
- public void setBackgroundColor(TerminalColor color) {
- printCodes(color.getBackgroundCode());
- }
-
- public void print(TerminalColor foregroundColor, String string) {
- setForegroundColor(foregroundColor);
- print(string);
- resetAll();
- }
-
- public void println(TerminalColor foregroundColor, String string) {
- print(foregroundColor, string);
- println();
- }
-
- public void print(TerminalColor foregroundColor, TerminalColor backgroundColor, String string) {
- setForegroundColor(foregroundColor);
- setBackgroundColor(backgroundColor);
- print(string);
- resetAll();
- }
-
- public void println(TerminalColor foregroundColor, TerminalColor backgroundColor, String string) {
- print(foregroundColor, backgroundColor, string);
- println();
- }
-
- public void print(TerminalColor foregroundColor, TerminalColor backgroundColor, EnumSet<TerminalStyle> styles, String string) {
- setForegroundColor(foregroundColor);
- setBackgroundColor(backgroundColor);
- setStyle(styles);
- print(string);
- resetAll();
- }
-
- public void println(TerminalColor foregroundColor, TerminalColor backgroundColor, EnumSet<TerminalStyle> styles, String string) {
- print(foregroundColor, backgroundColor, styles, string);
- println();
- }
-
- public void print(TerminalColor foregroundColor, TerminalColor backgroundColor, TerminalStyle style, String string) {
- print(foregroundColor, backgroundColor, EnumSet.of(style), string);
- }
-
- public void println(TerminalColor foregroundColor, TerminalColor backgroundColor, TerminalStyle style, String string) {
- print(foregroundColor, backgroundColor, style, string);
- println();
- }
-
- public void print(TerminalColor foregroundColor, EnumSet<TerminalStyle> styles, String string) {
- setForegroundColor(foregroundColor);
- setStyle(styles);
- print(string);
- resetAll();
- }
-
- public void println(TerminalColor foregroundColor, EnumSet<TerminalStyle> styles, String string) {
- print(foregroundColor, styles, string);
- println();
- }
-
- public void print(TerminalColor foregroundColor, TerminalStyle style, String string) {
- print(foregroundColor, EnumSet.of(style), string);
- }
-
- public void println(TerminalColor foregroundColor, TerminalStyle style, String string) {
- print(foregroundColor, style, string);
- println();
- }
-
- public void print(EnumSet<TerminalStyle> styles, String string) {
- setStyle(styles);
- print(string);
- resetAll();
- }
-
- public void println(EnumSet<TerminalStyle> styles, String string) {
- print(styles, string);
- println();
- }
-
- public void print(TerminalStyle style, String string) {
- print(EnumSet.of(style), string);
- }
-
- public void println(TerminalStyle style, String string) {
- print(style, string);
- println();
- }
-
- public void resetAll() {
- printCodes(TerminalStyle.Reset.code);
- }
-
- private void printCodes(int... codes) {
- if (COLOR_ENABLED && colorful) {
- print("\033[");
- for (int i = 0; i < codes.length; i++) {
- print(codes[i]);
- if (i < codes.length - 1 && codes.length > 1) {
- print(";");
- }
- }
- print("m");
- }
- }
-
- /**
- * Colors can be switched on/off during usage of this writer.
- *
- * @return whether colors are currently turned on
- * @see #isColorEnabled()
- */
- public boolean isColorful() {
- return colorful;
- }
-
- /**
- * Collors might be definitively disabled in constructor. If not, they can be turned on/off
- * during usage of this writer by {@linkplain #setColorful(boolean)}
- *
- * @return whether colors are allowed for this instance of this class
- * @see #isColorful()
- */
- public boolean isColorEnabled() {
- return COLOR_ENABLED;
- }
-
- /**
- * @see #isColorful()
- * @see #isColorEnabled()
- */
- public void setColorful(boolean colorful) {
- this.colorful = colorful;
- }
-
- public ColorfulPrintWriter(File file) throws FileNotFoundException {
- super(file);
- COLOR_ENABLED = true;
- }
-
- public ColorfulPrintWriter(OutputStream out) {
- super(out);
- COLOR_ENABLED = true;
- }
-
- public ColorfulPrintWriter(String fileName) throws FileNotFoundException {
- super(fileName);
- COLOR_ENABLED = true;
- }
-
- public ColorfulPrintWriter(Writer out) {
- super(out);
- COLOR_ENABLED = true;
- }
-
- public ColorfulPrintWriter(File file, String csn) throws FileNotFoundException, UnsupportedEncodingException {
- super(file, csn);
- COLOR_ENABLED = true;
- }
-
- /**
- * @param colorEnabled colors might be definitively disabled by this option – this might be more
- * optimalizable than dynamic turning off colors by {@linkplain #setColorful(boolean)} which is
- * not definitive (colors can be turned on during live of this instance). This might be useful
- * if you need an instance of this class but don't need colors at all.
- */
- public ColorfulPrintWriter(OutputStream out, boolean autoFlush, boolean colorEnabled) {
- super(out, autoFlush);
- COLOR_ENABLED = colorEnabled;
- }
-
- public ColorfulPrintWriter(String fileName, String csn) throws FileNotFoundException, UnsupportedEncodingException {
- super(fileName, csn);
- COLOR_ENABLED = true;
- }
-
- public ColorfulPrintWriter(Writer out, boolean autoFlush) {
- super(out, autoFlush);
- COLOR_ENABLED = true;
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/Constants.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk;
-
-import java.io.File;
-
-/**
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class Constants {
-
- public static final String PROGRAM_NAME = "SQL-DK";
- public static final String JAVA_PACKAGE = Constants.class.getPackage().getName();
- public static final String WEBSITE = "https://sql-dk.globalcode.info/";
- public static final String LICENSE_FILE = "info/globalcode/sql/dk/license.txt";
- public static final String VERSION_FILE = "info/globalcode/sql/dk/version.txt";
- public static final String HELP_FILE = "info/globalcode/sql/dk/help.txt";
- private static final File HOME_DIR = new File(System.getProperty("user.home"));
- /**
- * Directory where config and log files are stored.
- */
- public static final File DIR = new File(HOME_DIR, ".sql-dk"); // bash-completion:dir
- public static final File CONFIG_FILE = new File(DIR, "config.xml");
- public static final String EXAMPLE_CONFIG_FILE = "info/globalcode/sql/dk/example-config.xml";
-
- private Constants() {
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/DKException.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk;
-
-/**
- * TODO: GEC
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class DKException extends Exception {
-
- public DKException() {
- }
-
- public DKException(String message) {
- super(message);
- }
-
- public DKException(Throwable cause) {
- super(cause);
- }
-
- public DKException(String message, Throwable cause) {
- super(message, cause);
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/DatabaseConnection.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,192 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk;
-
-import static info.globalcode.sql.dk.jmx.ConnectionManagement.incrementCounter;
-import static info.globalcode.sql.dk.jmx.ConnectionManagement.resetCounter;
-import info.globalcode.sql.dk.batch.Batch;
-import info.globalcode.sql.dk.batch.BatchException;
-import info.globalcode.sql.dk.configuration.DatabaseDefinition;
-import info.globalcode.sql.dk.configuration.Loader;
-import info.globalcode.sql.dk.configuration.Properties;
-import info.globalcode.sql.dk.formatting.ColumnsHeader;
-import info.globalcode.sql.dk.formatting.Formatter;
-import info.globalcode.sql.dk.jmx.ConnectionManagement;
-import info.globalcode.sql.dk.jmx.ConnectionManagement.COUNTER;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.SQLWarning;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * Represents connected database. Is derived from {@linkplain DatabaseDefinition}.
- * Wraps {@linkplain Connection}.
- *
- * Is responsible for executing {@linkplain SQLCommand} and passing results to the
- * {@linkplain Formatter}.
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class DatabaseConnection implements AutoCloseable {
-
- private static final Logger log = Logger.getLogger(DatabaseConnection.class.getName());
- public static final String JDBC_PROPERTY_USER = "user";
- public static final String JDBC_PROPERTY_PASSWORD = "password";
- private final DatabaseDefinition databaseDefinition;
- private final Connection connection;
- private final Properties properties;
- /**
- * Could be null = JMX is disabled → must check, see functions in
- * {@linkplain ConnectionManagement}
- */
- private final ConnectionManagement connectionMBean;
-
- /**
- *
- * @param databaseDefinition DB url, name, password etc.
- * @param properties additional properties from CLI
- * @param connectionMBean JMX management bean | null = disabled JMX reporting
- * @throws SQLException
- */
- public DatabaseConnection(DatabaseDefinition databaseDefinition, Properties properties, ConnectionManagement connectionMBean) throws SQLException {
- this.databaseDefinition = databaseDefinition;
- this.properties = properties;
- this.connectionMBean = connectionMBean;
- this.connection = Loader.jdbcConnect(databaseDefinition, properties);
- }
-
- public void executeQuery(SQLCommand sqlCommand, Formatter formatter) throws SQLException {
- formatter.writeStartBatch();
- formatter.writeStartDatabase(databaseDefinition);
- formatter.writeStartStatement();
- formatter.writeQuery(sqlCommand.getQuery());
- formatter.writeParameters(sqlCommand.getParameters());
- processCommand(sqlCommand, formatter);
- formatter.writeEndStatement();
- formatter.writeEndDatabase();
- formatter.writeEndBatch();
- }
-
- public void executeBatch(Batch batch, Formatter formatter) throws SQLException, BatchException {
- formatter.writeStartBatch();
- formatter.writeStartDatabase(databaseDefinition);
- while (batch.hasNext()) {
- SQLCommand sqlCommand = batch.next();
- formatter.writeStartStatement();
- formatter.writeQuery(sqlCommand.getQuery());
- formatter.writeParameters(sqlCommand.getParameters());
- processCommand(sqlCommand, formatter);
- formatter.writeEndStatement();
- }
- formatter.writeEndDatabase();
- formatter.writeEndBatch();
- }
-
- private void processCommand(SQLCommand sqlCommand, Formatter formatter) throws SQLException {
- incrementCounter(connectionMBean, COUNTER.COMMAND);
- resetCounter(connectionMBean, COUNTER.RECORD_CURRENT);
-
- try (PreparedStatement ps = sqlCommand.prepareStatement(connection)) {
- log.log(Level.FINE, "Statement prepared");
- sqlCommand.parametrize(ps);
-
- boolean isRS = ps.execute();
- log.log(Level.FINE, "Statement executed");
- if (isRS) {
- try (ResultSet rs = ps.getResultSet()) {
- processResultSet(rs, formatter);
- }
- } else {
- processUpdateResult(ps, formatter);
- }
- logWarnings(ps);
-
- while (ps.getMoreResults() || ps.getUpdateCount() > -1) {
- ResultSet rs = ps.getResultSet();
- if (rs == null) {
- processUpdateResult(ps, formatter);
- } else {
- processResultSet(rs, formatter);
- rs.close();
- }
- logWarnings(ps);
- }
- }
- }
-
- private void processUpdateResult(PreparedStatement ps, Formatter formatter) throws SQLException {
- formatter.writeUpdatesResult(ps.getUpdateCount());
- }
-
- private void processResultSet(ResultSet rs, Formatter formatter) throws SQLException {
- formatter.writeStartResultSet(new ColumnsHeader(rs.getMetaData()));
-
- int columnCount = rs.getMetaData().getColumnCount();
-
- while (rs.next()) {
- incrementCounter(connectionMBean, COUNTER.RECORD_CURRENT);
- incrementCounter(connectionMBean, COUNTER.RECORD_TOTAL);
-
- formatter.writeStartRow();
-
- for (int i = 1; i <= columnCount; i++) {
- formatter.writeColumnValue(rs.getObject(i));
- }
-
- formatter.writeEndRow();
- }
-
- formatter.writeEndResultSet();
- }
-
- private void logWarnings(PreparedStatement ps) throws SQLException {
- SQLWarning w = ps.getWarnings();
- while (w != null) {
- log.log(Level.WARNING, "SQL: {0}", w.getLocalizedMessage());
- w = w.getNextWarning();
- }
- ps.clearWarnings();
- }
-
- /**
- * Tests if this connection is live.
- *
- * @return true if test was successful
- * @throws SQLException if test fails
- */
- public boolean test() throws SQLException {
- connection.getAutoCommit();
- return true;
- }
-
- public String getProductName() throws SQLException {
- return connection.getMetaData().getDatabaseProductName();
- }
-
- public String getProductVersion() throws SQLException {
- return connection.getMetaData().getDatabaseProductVersion();
- }
-
- @Override
- public void close() throws SQLException {
- connection.close();
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/Functions.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,254 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk;
-
-import info.globalcode.sql.dk.configuration.NameIdentified;
-import info.globalcode.sql.dk.configuration.PropertyDeclaration;
-import info.globalcode.sql.dk.configuration.PropertyDeclarations;
-import info.globalcode.sql.dk.formatting.Formatter;
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class Functions {
-
- private static final String NBSP = " ";
- private static final Pattern WHITESPACE_TO_REPLACE = Pattern.compile("\\n|\\r|\\t|" + NBSP);
-
- private Functions() {
- }
-
- public static boolean equalz(Object a, Object b) {
- return a == null ? b == null : a.equals(b);
- }
-
- /**
- *
- * @param text String to be examinated
- * @param trim whether text should be trimmed before examination
- * @return whether text is not empty and one or more characters long (after prospective trim)
- */
- public static boolean isEmpty(String text, boolean trim) {
- if (text == null) {
- return true;
- } else {
- if (trim) {
- text = text.trim();
- }
- return text.isEmpty();
- }
- }
-
- /**
- * @see #isEmpty(java.lang.String, boolean)
- */
- public static boolean isNotEmpty(String text, boolean trim) {
- return !isEmpty(text, trim);
- }
-
- public boolean isEmpty(Collection c) {
- return c == null || c.isEmpty();
- }
-
- public boolean isNotEmpty(Collection c) {
- return !isEmpty(c);
- }
-
- public boolean isEmpty(Map m) {
- return m == null || m.isEmpty();
- }
-
- public boolean isNotEmpty(Map m) {
- return !isEmpty(m);
- }
-
- /**
- * @return empty collection if given one is null | or the original one
- */
- public static <T> Collection<T> notNull(Collection<T> c) {
- if (c == null) {
- return Collections.emptyList();
- } else {
- return c;
- }
- }
-
- public static <T extends NameIdentified> T findByName(Collection<T> collection, String name) {
- for (T element : notNull(collection)) {
- if (element != null && equalz(element.getName(), name)) {
- return element;
- }
- }
-
- return null;
- }
-
- /**
- * Copy file from Java resources to file system.
- */
- public static void installResource(String resourceName, File target) throws IOException {
- try (BufferedReader reader = new BufferedReader(new InputStreamReader(Functions.class.getClassLoader().getResourceAsStream(resourceName)))) {
- try (PrintWriter writer = new PrintWriter(target)) {
- while (true) {
- String line = reader.readLine();
- if (line == null) {
- break;
- } else {
- writer.println(line);
- }
- }
- }
- }
- }
-
- public static String rpad(String s, int n) {
- if (n > 0) {
- return String.format("%1$-" + n + "s", s);
- } else {
- return s;
- }
- }
-
- public static String lpad(String s, int n) {
- if (n > 0) {
- return String.format("%1$" + n + "s", s);
- } else {
- return s;
- }
- }
-
- public static String repeat(char ch, int count) {
- char[] array = new char[count];
- Arrays.fill(array, ch);
- return new String(array);
- }
- private final static char[] HEX_ALPHABET = "0123456789abcdef".toCharArray();
-
- public static String toHex(byte[] bytes) {
- char[] hexChars = new char[bytes.length * 2];
- for (int j = 0; j < bytes.length; j++) {
- int v = bytes[j] & 0xFF;
- hexChars[j * 2] = HEX_ALPHABET[v >>> 4];
- hexChars[j * 2 + 1] = HEX_ALPHABET[v & 0x0F];
- }
- return new String(hexChars);
- }
-
- public static String readString(InputStream in) throws IOException {
- try (BufferedReader br = new BufferedReader(new InputStreamReader(in))) {
- StringBuilder result = new StringBuilder();
- for (String line = br.readLine(); line != null; line = br.readLine()) {
- result.append(line);
- result.append('\n');
- }
- return result.toString();
- }
- }
-
- /**
- * @param <P> type of the last parent
- * @param <T> type of the examined class
- * @param type examined class
- * @param lastParent the last parent type to stop at
- * @return list of types starting with <code>type</code> and ending with <code>lastParent</code>
- */
- public static <P, T extends P> List<Class<? extends P>> getClassHierarchy(Class<T> type, Class<P> lastParent) {
- List<Class<? extends P>> hierarchy = new ArrayList<>();
-
- for (Class current = type; current != null && lastParent.isAssignableFrom(current); current = current.getSuperclass()) {
- hierarchy.add(current);
- }
-
- return hierarchy;
- }
-
- public static PropertyDeclaration[] getPropertyDeclarations(Class<? extends Formatter> formatterClass) {
- PropertyDeclarations properties = formatterClass.getAnnotation(PropertyDeclarations.class);
-
- if (properties == null) {
- PropertyDeclaration p = formatterClass.getAnnotation(PropertyDeclaration.class);
- return p == null ? new PropertyDeclaration[]{} : new PropertyDeclaration[]{p};
- } else {
- return properties.value();
- }
- }
-
- /**
- * TODO: support background or styles and move to ColorfulPrintWriter
- *
- * @param out
- * @param valueString
- * @param basicColor
- * @param escapeColor
- */
- public static void printValueWithWhitespaceReplaced(ColorfulPrintWriter out, String valueString, ColorfulPrintWriter.TerminalColor basicColor, ColorfulPrintWriter.TerminalColor escapeColor) {
-
- Matcher m = WHITESPACE_TO_REPLACE.matcher(valueString);
-
- int start = 0;
-
- while (m.find(start)) {
-
- printColorOrNot(out, basicColor, valueString.substring(start, m.start()));
-
- switch (m.group()) {
- case "\n":
- out.print(escapeColor, "↲");
- break;
- case "\r":
- out.print(escapeColor, "⏎");
- break;
- case "\t":
- out.print(escapeColor, "↹");
- break;
- case NBSP:
- out.print(escapeColor, "⎵");
- break;
- default:
- throw new IllegalStateException("Unexpected whitespace token: „" + m.group() + "“");
- }
-
- start = m.end();
- }
-
- printColorOrNot(out, basicColor, valueString.substring(start, valueString.length()));
- }
-
- private static void printColorOrNot(ColorfulPrintWriter out, ColorfulPrintWriter.TerminalColor color, String text) {
- if (color == null) {
- out.print(text);
- } else {
- out.print(color, text);
- }
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/InfoLister.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,673 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk;
-
-import info.globalcode.sql.dk.configuration.CommandArgument;
-import info.globalcode.sql.dk.configuration.Configuration;
-import info.globalcode.sql.dk.configuration.ConfigurationException;
-import info.globalcode.sql.dk.configuration.ConfigurationProvider;
-import info.globalcode.sql.dk.configuration.DatabaseDefinition;
-import info.globalcode.sql.dk.configuration.FormatterDefinition;
-import info.globalcode.sql.dk.configuration.Properties;
-import info.globalcode.sql.dk.configuration.Property;
-import info.globalcode.sql.dk.configuration.PropertyDeclaration;
-import info.globalcode.sql.dk.configuration.TunnelDefinition;
-import info.globalcode.sql.dk.formatting.ColumnsHeader;
-import info.globalcode.sql.dk.formatting.CommonProperties;
-import info.globalcode.sql.dk.formatting.FakeSqlArray;
-import info.globalcode.sql.dk.formatting.Formatter;
-import info.globalcode.sql.dk.formatting.FormatterContext;
-import info.globalcode.sql.dk.formatting.FormatterException;
-import java.io.BufferedReader;
-import java.io.ByteArrayOutputStream;
-import java.io.InputStreamReader;
-import java.io.PrintStream;
-import java.sql.Array;
-import java.sql.Driver;
-import java.sql.DriverManager;
-import java.sql.DriverPropertyInfo;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.ServiceLoader;
-import java.util.Set;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
-import java.util.logging.Level;
-import java.util.logging.LogRecord;
-import java.util.logging.Logger;
-import javax.sql.rowset.RowSetMetaDataImpl;
-
-/**
- * Displays info like help, version etc.
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class InfoLister {
-
- private static final Logger log = Logger.getLogger(InfoLister.class.getName());
- /**
- * Fake database name for output formatting
- */
- public static final String CONFIG_DB_NAME = "sqldk_configuration";
- private final PrintStream out;
- private final ConfigurationProvider configurationProvider;
- private final CLIOptions options;
- private Formatter formatter;
-
- public InfoLister(PrintStream out, ConfigurationProvider configurationProvider, CLIOptions options) {
- this.out = out;
- this.configurationProvider = configurationProvider;
- this.options = options;
- }
-
- public void showInfo() throws ConfigurationException, FormatterException {
- EnumSet<InfoType> commands = options.getShowInfo();
-
- boolean formattinNeeded = false;
-
- for (InfoType infoType : commands) {
- switch (infoType) {
- case CONNECTION:
- case JDBC_DRIVERS:
- case JDBC_PROPERTIES:
- case DATABASES:
- case FORMATTERS:
- case FORMATTER_PROPERTIES:
- case TYPES:
- case JAVA_PROPERTIES:
- case ENVIRONMENT_VARIABLES:
- formattinNeeded = true;
- break;
- }
- }
-
- if (formattinNeeded) {
- try (Formatter f = getFormatter()) {
- formatter = f;
- formatter.writeStartBatch();
- DatabaseDefinition dd = new DatabaseDefinition();
- dd.setName(CONFIG_DB_NAME);
- formatter.writeStartDatabase(dd);
- showInfos(commands);
- formatter.writeEndDatabase();
- formatter.writeEndBatch();
- formatter.close();
- }
- } else {
- showInfos(commands);
- }
- }
-
- private void showInfos(EnumSet<InfoType> commands) throws ConfigurationException, FormatterException {
- for (InfoType infoType : commands) {
- infoType.showInfo(this);
- }
- }
-
- private void listJavaProperties() throws FormatterException, ConfigurationException {
- ColumnsHeader header = constructHeader(new HeaderField("name", SQLType.VARCHAR), new HeaderField("value", SQLType.VARCHAR));
- List<Object[]> data = new ArrayList<>();
- for (Entry<Object, Object> e : System.getProperties().entrySet()) {
- data.add(new Object[]{e.getKey(), e.getValue()});
- }
- printTable(formatter, header, "-- Java system properties", null, data, 0);
- }
-
- private void listEnvironmentVariables() throws FormatterException, ConfigurationException {
- ColumnsHeader header = constructHeader(new HeaderField("name", SQLType.VARCHAR), new HeaderField("value", SQLType.VARCHAR));
- List<Object[]> data = new ArrayList<>();
- for (Entry<String, String> e : System.getenv().entrySet()) {
- data.add(new Object[]{e.getKey(), e.getValue()});
- }
- printTable(formatter, header, "-- environment variables", null, data, 0);
- }
-
- private void listFormatters() throws ConfigurationException, FormatterException {
- ColumnsHeader header = constructHeader(
- new HeaderField("name", SQLType.VARCHAR),
- new HeaderField("built_in", SQLType.BOOLEAN),
- new HeaderField("default", SQLType.BOOLEAN),
- new HeaderField("class_name", SQLType.VARCHAR),
- new HeaderField("valid", SQLType.BOOLEAN));
- List<Object[]> data = new ArrayList<>();
-
- String defaultFormatter = configurationProvider.getConfiguration().getDefaultFormatter();
- defaultFormatter = defaultFormatter == null ? Configuration.DEFAULT_FORMATTER : defaultFormatter;
-
- for (FormatterDefinition fd : configurationProvider.getConfiguration().getBuildInFormatters()) {
- data.add(new Object[]{fd.getName(), true, defaultFormatter.equals(fd.getName()), fd.getClassName(), isInstantiable(fd)});
- }
-
- for (FormatterDefinition fd : configurationProvider.getConfiguration().getFormatters()) {
- data.add(new Object[]{fd.getName(), false, defaultFormatter.equals(fd.getName()), fd.getClassName(), isInstantiable(fd)});
- }
-
- printTable(formatter, header, "-- configured and built-in output formatters", null, data);
- }
-
- private boolean isInstantiable(FormatterDefinition fd) {
- try {
- try (ByteArrayOutputStream testStream = new ByteArrayOutputStream()) {
- fd.getInstance(new FormatterContext(testStream, new Properties(0)));
- return true;
- }
- } catch (Exception e) {
- log.log(Level.SEVERE, "Unable to create an instance of formatter: " + fd.getName(), e);
- return false;
- }
- }
-
- private void listFormatterProperties() throws FormatterException, ConfigurationException {
- for (String formatterName : options.getFormatterNamesToListProperties()) {
- listFormatterProperties(formatterName);
- }
- }
-
- private void listFormatterProperties(String formatterName) throws FormatterException, ConfigurationException {
- FormatterDefinition fd = configurationProvider.getConfiguration().getFormatter(formatterName);
- try {
-
- // currently only for debugging purposes
- // TODO: introduce --info-lister-property or generic filtering capability in printTable() ?
- boolean printDeclaredIn = options.getFormatterProperties().getBoolean("InfoLister:print:declared_in", false);
-
- List<HeaderField> headerFields = new ArrayList<>();
- headerFields.add(new HeaderField("name", SQLType.VARCHAR));
- headerFields.add(new HeaderField("type", SQLType.VARCHAR));
- headerFields.add(new HeaderField("default", SQLType.VARCHAR));
- headerFields.add(new HeaderField("description", SQLType.VARCHAR));
- if (printDeclaredIn) {
- headerFields.add(new HeaderField("declared_in", SQLType.VARCHAR));
- }
-
- ColumnsHeader header = constructHeader(headerFields.toArray(new HeaderField[0]));
-
- Map<String, Object[]> data = new HashMap<>();
- Class<Formatter> formatterClass = (Class<Formatter>) Class.forName(fd.getClassName());
- List<Class<? extends Formatter>> hierarchy = Functions.getClassHierarchy(formatterClass, Formatter.class);
- Collections.reverse(hierarchy);
- hierarchy.stream().forEach((c) -> {
- for (PropertyDeclaration p : Functions.getPropertyDeclarations(c)) {
- data.put(p.name(), propertyDeclarationToRow(p, c, printDeclaredIn));
- }
- });
-
- List<Parameter> parameters = new ArrayList<>();
- parameters.add(new NamedParameter("formatter", formatterName, SQLType.VARCHAR));
-
- printTable(formatter, header, "-- formatter properties", parameters, new ArrayList<>(data.values()));
- } catch (ClassNotFoundException e) {
- throw new ConfigurationException("Unable to find class " + fd.getClassName() + " of formatter" + fd.getName(), e);
- }
- }
-
- private static Object[] propertyDeclarationToRow(PropertyDeclaration p, Class formatterClass, boolean printDeclaredIn) {
- List list = new ArrayList();
-
- list.add(p.name());
- list.add(CommonProperties.getSimpleTypeName(p.type()));
- list.add(p.defaultValue());
- list.add(p.description());
- if (printDeclaredIn) {
- list.add(formatterClass.getName());
- }
-
- return list.toArray();
- }
-
- private void listTypes() throws FormatterException, ConfigurationException {
- ColumnsHeader header = constructHeader(new HeaderField("name", SQLType.VARCHAR), new HeaderField("code", SQLType.INTEGER));
- List<Object[]> data = new ArrayList<>();
- for (SQLType sqlType : SQLType.values()) {
- data.add(new Object[]{sqlType.name(), sqlType.getCode()});
- }
- printTable(formatter, header, "-- data types", null, data);
- log.log(Level.INFO, "Type names in --types option are case insensitive");
- }
-
- private void listDatabases() throws ConfigurationException, FormatterException {
- ColumnsHeader header = constructHeader(
- new HeaderField("database_name", SQLType.VARCHAR),
- new HeaderField("user_name", SQLType.VARCHAR),
- new HeaderField("database_url", SQLType.VARCHAR));
- List<Object[]> data = new ArrayList<>();
-
- final List<DatabaseDefinition> configuredDatabases = configurationProvider.getConfiguration().getDatabases();
- if (configuredDatabases.isEmpty()) {
- log.log(Level.WARNING, "No databases are configured.");
- } else {
- for (DatabaseDefinition dd : configuredDatabases) {
- data.add(new Object[]{dd.getName(), dd.getUserName(), dd.getUrl()});
-
- final TunnelDefinition tunnel = dd.getTunnel();
- if (tunnel != null) {
- log.log(Level.INFO, "Tunnel command: {0}", tunnel.getCommand());
- for (CommandArgument ca : Functions.notNull(tunnel.getArguments())) {
- log.log(Level.INFO, "\targument: {0}/{1}", new Object[]{ca.getType(), ca.getValue()});
- }
- }
-
- }
- }
-
- printTable(formatter, header, "-- configured databases", null, data);
- }
-
- private void listJdbcDrivers() throws FormatterException, ConfigurationException {
- ColumnsHeader header = constructHeader(
- new HeaderField("class", SQLType.VARCHAR),
- new HeaderField("version", SQLType.VARCHAR),
- new HeaderField("major", SQLType.INTEGER),
- new HeaderField("minor", SQLType.INTEGER),
- new HeaderField("jdbc_compliant", SQLType.BOOLEAN));
- List<Object[]> data = new ArrayList<>();
-
- final ServiceLoader<Driver> drivers = ServiceLoader.load(Driver.class);
- for (Driver d : drivers) {
- data.add(new Object[]{
- d.getClass().getName(),
- d.getMajorVersion() + "." + d.getMinorVersion(),
- d.getMajorVersion(),
- d.getMinorVersion(),
- d.jdbcCompliant()
- });
- }
-
- printTable(formatter, header, "-- discovered JDBC drivers (available on the CLASSPATH)", null, data);
- }
-
- private void listJdbcProperties() throws FormatterException, ConfigurationException {
- for (String dbName : options.getDatabaseNamesToListProperties()) {
- ColumnsHeader header = constructHeader(
- new HeaderField("property_name", SQLType.VARCHAR),
- new HeaderField("required", SQLType.BOOLEAN),
- new HeaderField("choices", SQLType.ARRAY),
- new HeaderField("configured_value", SQLType.VARCHAR),
- new HeaderField("description", SQLType.VARCHAR));
- List<Object[]> data = new ArrayList<>();
-
- DatabaseDefinition dd = configurationProvider.getConfiguration().getDatabase(dbName);
-
- Driver driver = findDriver(dd);
-
- if (driver == null) {
- log.log(Level.WARNING, "No JDBC driver was found for DB: {0} with URL: {1}", new Object[]{dd.getName(), dd.getUrl()});
- } else {
- log.log(Level.INFO, "For DB: {0} was found JDBC driver: {1}", new Object[]{dd.getName(), driver.getClass().getName()});
-
- try {
- DriverPropertyInfo[] propertyInfos = driver.getPropertyInfo(dd.getUrl(), dd.getProperties().getJavaProperties());
-
- Set<String> standardProperties = new HashSet<>();
-
- for (DriverPropertyInfo pi : propertyInfos) {
- Array choices = new FakeSqlArray(pi.choices, SQLType.VARCHAR);
- data.add(new Object[]{
- pi.name,
- pi.required,
- choices.getArray() == null ? "" : choices,
- pi.value == null ? "" : pi.value,
- pi.description
- });
- standardProperties.add(pi.name);
- }
-
- for (Property p : dd.getProperties()) {
- if (!standardProperties.contains(p.getName())) {
- data.add(new Object[]{
- p.getName(),
- "",
- "",
- p.getValue(),
- ""
- });
- log.log(Level.WARNING, "Your configuration contains property „{0}“ not declared by the JDBC driver.", p.getName());
- }
- }
-
- } catch (SQLException e) {
- log.log(Level.WARNING, "Error during getting property infos.", e);
- }
-
- List<Parameter> parameters = new ArrayList<>();
- parameters.add(new NamedParameter("database", dbName, SQLType.VARCHAR));
- parameters.add(new NamedParameter("driver_class", driver.getClass().getName(), SQLType.VARCHAR));
- parameters.add(new NamedParameter("driver_major_version", driver.getMajorVersion(), SQLType.INTEGER));
- parameters.add(new NamedParameter("driver_minor_version", driver.getMinorVersion(), SQLType.INTEGER));
-
- printTable(formatter, header, "-- configured and configurable JDBC driver properties", parameters, data);
- }
- }
-
- }
-
- private Driver findDriver(DatabaseDefinition dd) {
- final ServiceLoader<Driver> drivers = ServiceLoader.load(Driver.class);
- for (Driver d : drivers) {
- try {
- if (d.acceptsURL(dd.getUrl())) {
- return d;
- }
- } catch (SQLException e) {
- log.log(Level.WARNING, "Error during finding JDBC driver for: " + dd.getName(), e);
- }
- }
- return null;
- }
-
- /**
- * Parallelism for connection testing – maximum concurrent database connections.
- */
- private static final int TESTING_THREAD_COUNT = 64;
- /**
- * Time limit for all connection testing threads – particular timeouts per connection will be
- * much smaller.
- */
- private static final long TESTING_AWAIT_LIMIT = 1;
- private static final TimeUnit TESTING_AWAIT_UNIT = TimeUnit.DAYS;
-
- private void testConnections() throws FormatterException, ConfigurationException {
- ColumnsHeader header = constructHeader(
- new HeaderField("database_name", SQLType.VARCHAR),
- new HeaderField("configured", SQLType.BOOLEAN),
- new HeaderField("connected", SQLType.BOOLEAN),
- new HeaderField("product_name", SQLType.VARCHAR),
- new HeaderField("product_version", SQLType.VARCHAR));
-
- log.log(Level.FINE, "Testing DB connections in {0} threads", TESTING_THREAD_COUNT);
-
- ExecutorService es = Executors.newFixedThreadPool(TESTING_THREAD_COUNT);
-
- final Formatter currentFormatter = formatter;
-
- printHeader(currentFormatter, header, "-- database configuration and connectivity test", null);
-
- for (final String dbName : options.getDatabaseNamesToTest()) {
- preloadDriver(dbName);
- }
-
- for (final String dbName : options.getDatabaseNamesToTest()) {
- es.submit(() -> {
- final Object[] row = testConnection(dbName);
- synchronized (currentFormatter) {
- printRow(currentFormatter, row);
- }
- }
- );
- }
-
- es.shutdown();
-
- try {
- log.log(Level.FINEST, "Waiting for test results: {0} {1}", new Object[]{TESTING_AWAIT_LIMIT, TESTING_AWAIT_UNIT.name()});
- boolean finished = es.awaitTermination(TESTING_AWAIT_LIMIT, TESTING_AWAIT_UNIT);
- if (finished) {
- log.log(Level.FINEST, "All testing threads finished in time limit.");
- } else {
- throw new FormatterException("Exceeded total time limit for test threads – this should never happen");
- }
- } catch (InterruptedException e) {
- throw new FormatterException("Interrupted while waiting for test results", e);
- }
-
- printFooter(currentFormatter);
- }
-
- /**
- * JDBC driver classes should be preloaded in single thread to avoid deadlocks while doing
- * {@linkplain DriverManager#registerDriver(java.sql.Driver)} during parallel connections.
- *
- * @param dbName
- */
- private void preloadDriver(String dbName) {
- try {
- DatabaseDefinition dd = configurationProvider.getConfiguration().getDatabase(dbName);
- Driver driver = findDriver(dd);
- if (driver == null) {
- log.log(Level.WARNING, "No Driver found for DB: {0}", dbName);
- } else {
- log.log(Level.FINEST, "Driver preloading for DB: {0} was successfull", dbName);
- }
- } catch (Exception e) {
- LogRecord r = new LogRecord(Level.WARNING, "Failed to preload the Driver for DB: {0}");
- r.setParameters(new Object[]{dbName});
- r.setThrown(e);
- log.log(r);
- }
- }
-
- private Object[] testConnection(String dbName) {
- log.log(Level.FINE, "Testing connection to database: {0}", dbName);
-
- boolean succesfullyConnected = false;
- boolean succesfullyConfigured = false;
- String productName = null;
- String productVersion = null;
-
- try {
- DatabaseDefinition dd = configurationProvider.getConfiguration().getDatabase(dbName);
- log.log(Level.FINE, "Database definition was loaded from configuration");
- succesfullyConfigured = true;
- try (DatabaseConnection dc = dd.connect(options.getDatabaseProperties())) {
- succesfullyConnected = dc.test();
- productName = dc.getProductName();
- productVersion = dc.getProductVersion();
- }
- log.log(Level.FINE, "Database connection test was successful");
- } catch (ConfigurationException | SQLException | RuntimeException e) {
- log.log(Level.SEVERE, "Error during testing connection " + dbName, e);
- }
-
- return new Object[]{dbName, succesfullyConfigured, succesfullyConnected, productName, productVersion};
- }
-
- private void printResource(String fileName) {
- try (BufferedReader reader = new BufferedReader(new InputStreamReader(getClass().getClassLoader().getResourceAsStream(fileName)))) {
- while (true) {
- String line = reader.readLine();
- if (line == null) {
- break;
- } else {
- println(line);
- }
- }
- } catch (Exception e) {
- log.log(Level.SEVERE, "Unable to print this info. Please see our website for it: " + Constants.WEBSITE, e);
- }
- }
-
- private void println(String line) {
- out.println(line);
- }
-
- private void printTable(Formatter formatter, ColumnsHeader header, String sql, List<Parameter> parameters, List<Object[]> data) throws ConfigurationException, FormatterException {
- printTable(formatter, header, sql, parameters, data, null);
- }
-
- private void printTable(Formatter formatter, ColumnsHeader header, String sql, List<Parameter> parameters, List<Object[]> data, final Integer sortByColumn) throws ConfigurationException, FormatterException {
- printHeader(formatter, header, sql, parameters);
-
- if (sortByColumn != null) {
- Collections.sort(data, new Comparator<Object[]>() {
-
- @Override
- public int compare(Object[] o1, Object[] o2) {
- String s1 = String.valueOf(o1[sortByColumn]);
- String s2 = String.valueOf(o2[sortByColumn]);
- return s1.compareTo(s2);
- }
- });
- }
-
- for (Object[] row : data) {
- printRow(formatter, row);
- }
-
- printFooter(formatter);
- }
-
- private void printHeader(Formatter formatter, ColumnsHeader header, String sql, List<Parameter> parameters) {
- formatter.writeStartStatement();
- if (sql != null) {
- formatter.writeQuery(sql);
- if (parameters != null) {
- formatter.writeParameters(parameters);
- }
- }
- formatter.writeStartResultSet(header);
- }
-
- private void printRow(Formatter formatter, Object[] row) {
- formatter.writeStartRow();
- for (Object cell : row) {
- formatter.writeColumnValue(cell);
- }
- formatter.writeEndRow();
- }
-
- private void printFooter(Formatter formatter) {
- formatter.writeEndResultSet();
- formatter.writeEndStatement();
- }
-
- private Formatter getFormatter() throws ConfigurationException, FormatterException {
- String formatterName = options.getFormatterName();
- formatterName = formatterName == null ? Configuration.DEFAULT_FORMATTER_PREFETCHING : formatterName;
- FormatterDefinition fd = configurationProvider.getConfiguration().getFormatter(formatterName);
- FormatterContext context = new FormatterContext(out, options.getFormatterProperties());
- return fd.getInstance(context);
- }
-
- private ColumnsHeader constructHeader(HeaderField... fields) throws FormatterException {
- try {
- RowSetMetaDataImpl metaData = new RowSetMetaDataImpl();
- metaData.setColumnCount(fields.length);
-
- for (int i = 0; i < fields.length; i++) {
- HeaderField hf = fields[i];
- int sqlIndex = i + 1;
- metaData.setColumnName(sqlIndex, hf.name);
- metaData.setColumnLabel(sqlIndex, hf.name);
- metaData.setColumnType(sqlIndex, hf.type.getCode());
- metaData.setColumnTypeName(sqlIndex, hf.type.name());
- }
-
- return new ColumnsHeader(metaData);
- } catch (SQLException e) {
- throw new FormatterException("Error while constructing table headers", e);
- }
- }
-
- private static class HeaderField {
-
- String name;
- SQLType type;
-
- public HeaderField(String name, SQLType type) {
- this.name = name;
- this.type = type;
- }
- }
-
- public enum InfoType {
-
- HELP {
- @Override
- public void showInfo(InfoLister infoLister) {
- infoLister.printResource(Constants.HELP_FILE);
- }
- },
- VERSION {
- @Override
- public void showInfo(InfoLister infoLister) {
- infoLister.printResource(Constants.VERSION_FILE);
- }
- },
- LICENSE {
- @Override
- public void showInfo(InfoLister infoLister) {
- infoLister.printResource(Constants.LICENSE_FILE);
- }
- },
- JAVA_PROPERTIES {
- @Override
- public void showInfo(InfoLister infoLister) throws FormatterException, ConfigurationException {
- infoLister.listJavaProperties();
- }
- },
- ENVIRONMENT_VARIABLES {
- @Override
- public void showInfo(InfoLister infoLister) throws FormatterException, ConfigurationException {
- infoLister.listEnvironmentVariables();
- }
- },
- FORMATTERS {
- @Override
- public void showInfo(InfoLister infoLister) throws FormatterException, ConfigurationException {
- infoLister.listFormatters();
- }
- },
- FORMATTER_PROPERTIES {
- @Override
- public void showInfo(InfoLister infoLister) throws FormatterException, ConfigurationException {
- infoLister.listFormatterProperties();
- }
- },
- TYPES {
- @Override
- public void showInfo(InfoLister infoLister) throws FormatterException, ConfigurationException {
- infoLister.listTypes();
- }
- },
- JDBC_DRIVERS {
- @Override
- public void showInfo(InfoLister infoLister) throws ConfigurationException, FormatterException {
- infoLister.listJdbcDrivers();
- }
- },
- JDBC_PROPERTIES {
- @Override
- public void showInfo(InfoLister infoLister) throws ConfigurationException, FormatterException {
- infoLister.listJdbcProperties();
- }
- },
- DATABASES {
- @Override
- public void showInfo(InfoLister infoLister) throws FormatterException, ConfigurationException {
- infoLister.listDatabases();
- }
- },
- CONNECTION {
- @Override
- public void showInfo(InfoLister infoLister) throws FormatterException, ConfigurationException {
- infoLister.testConnections();
- }
- };
-
- public abstract void showInfo(InfoLister infoLister) throws ConfigurationException, FormatterException;
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/InvalidOptionsException.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-
-/**
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class InvalidOptionsException extends Exception {
-
- private final Collection<OptionProblem> problems = new ArrayList<>();
-
- public Collection<OptionProblem> getProblems() {
- return Collections.unmodifiableCollection(problems);
- }
-
- public void addProblem(OptionProblem p) {
- problems.add(p);
- }
-
- public boolean hasProblems() {
- return !problems.isEmpty();
- }
-
- public static class OptionProblem {
-
- private String description;
- private Throwable exception;
-
- public OptionProblem(String description) {
- this.description = description;
- }
-
- public OptionProblem(String description, Throwable exception) {
- this.description = description;
- this.exception = exception;
- }
-
- public String getDescription() {
- return description;
- }
-
- public Throwable getException() {
- return exception;
- }
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/NamedParameter.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk;
-
-import info.globalcode.sql.dk.configuration.NameIdentified;
-
-/**
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class NamedParameter extends Parameter implements NameIdentified {
-
- private String name;
-
- public NamedParameter(String name, Object value, SQLType type) {
- super(value, type);
- this.name = name;
- }
-
- @Override
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- @Override
- public String toString() {
- return "NamedParameter {" + name + " = " + getValue() + "; " + getType() + "}";
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/Parameter.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk;
-
-import java.sql.Types;
-
-/**
- * Parameter for {@linkplain SQLCommand}
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class Parameter {
-
- /**
- * @see Types
- */
- public static final SQLType DEFAULT_TYPE = SQLType.VARCHAR;
- private Object value;
- private SQLType type;
-
- public Parameter() {
- }
-
- public Parameter(Object value, SQLType type) {
- this.value = value;
- if (type == null) {
- this.type = DEFAULT_TYPE;
- } else {
- this.type = type;
- }
- }
-
- public Object getValue() {
- return value;
- }
-
- public void setValue(Object value) {
- this.value = value;
- }
-
- /**
- * @see java.sql.Types
- */
- public SQLType getType() {
- return type;
- }
-
- /**
- * @see java.sql.Types
- */
- public void setType(SQLType type) {
- this.type = type;
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/SQLCommand.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk;
-
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-import java.util.List;
-
-/**
- * Represents SQL string and its parameters (if there are any).
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public abstract class SQLCommand {
-
- private String query;
-
- public SQLCommand(String query) {
- this.query = query;
- }
-
- public PreparedStatement prepareStatement(Connection c) throws SQLException {
- return c.prepareStatement(query);
- }
-
- public abstract void parametrize(PreparedStatement ps) throws SQLException;
-
- public abstract List<? extends Parameter> getParameters();
-
- public String getQuery() {
- return query;
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/SQLCommandNamed.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,155 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk;
-
-import static info.globalcode.sql.dk.Functions.findByName;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.regex.PatternSyntaxException;
-
-/**
- * Has named parameters.
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class SQLCommandNamed extends SQLCommand {
-
- private static final Logger log = Logger.getLogger(SQLCommandNamed.class.getName());
- private String namePrefix;
- private String nameSuffix;
- private List<NamedParameter> parameters;
- private List<NamedParameter> parametersUsed = new ArrayList<>();
- private StringBuilder updatedQuery;
- private Pattern pattern;
- private SQLCommandNumbered numbered;
-
- public SQLCommandNamed(String query, List<NamedParameter> parameters, String namePrefix, String nameSuffix) {
- super(query);
- this.updatedQuery = new StringBuilder(query.length());
- this.parameters = parameters;
- this.namePrefix = namePrefix;
- this.nameSuffix = nameSuffix;
- }
-
- @Override
- public PreparedStatement prepareStatement(Connection c) throws SQLException {
- return getSQLCommandNumbered().prepareStatement(c);
- }
-
- @Override
- public void parametrize(PreparedStatement ps) throws SQLException {
- getSQLCommandNumbered().parametrize(ps);
- }
-
- private void prepare() throws SQLException {
- try {
- buildPattern();
- placeParametersAndUpdateQuery();
- logPossiblyMissingParameters();
- } catch (PatternSyntaxException e) {
- throw new SQLException("Name prefix „" + namePrefix + "“ or suffix „" + nameSuffix + "“ contain a wrong regular expression. " + e.getLocalizedMessage(), e);
- }
- }
-
- /**
- * @return SQL command with named parameters converted to SQL command with numbered parameters
- */
- public SQLCommandNumbered getSQLCommandNumbered() throws SQLException {
- if (numbered == null) {
- prepare();
- numbered = new SQLCommandNumbered(updatedQuery.toString(), parametersUsed);
- }
-
- return numbered;
- }
-
- /**
- * Builds a regexp pattern that matches all parameter names (with prefix/suffix) and which has
- * one group: parameter name (without prefix/suffix)
- */
- private void buildPattern() throws PatternSyntaxException {
- StringBuilder patternString = new StringBuilder();
-
- patternString.append(namePrefix);
- patternString.append("(?<paramName>");
- for (int i = 0; i < parameters.size(); i++) {
- patternString.append(Pattern.quote(parameters.get(i).getName()));
- if (i < parameters.size() - 1) {
- patternString.append("|");
- }
- }
- patternString.append(")");
- patternString.append(nameSuffix);
-
- pattern = Pattern.compile(patternString.toString());
- }
-
- private void placeParametersAndUpdateQuery() {
- final String originalQuery = getQuery();
- Matcher m = pattern.matcher(originalQuery);
-
- int lastPosition = 0;
- while (m.find(lastPosition)) {
- String name = m.group("paramName");
-
- updatedQuery.append(originalQuery.substring(lastPosition, m.start()));
- updatedQuery.append("?");
-
- parametersUsed.add(findByName(parameters, name));
-
- lastPosition = m.end();
- }
- updatedQuery.append(originalQuery.substring(lastPosition, originalQuery.length()));
-
- for (NamedParameter definedParameter : parameters) {
- if (findByName(parametersUsed, definedParameter.getName()) == null) {
- /**
- * User can have predefined set of parameters and use them with different SQL
- * queries that use only subset of these parameters → just warning, not exception.
- */
- log.log(Level.WARNING, "Parameter „{0}“ is defined but not used in the query: „{1}“", new Object[]{definedParameter.getName(), originalQuery});
- }
- }
- }
-
- private void logPossiblyMissingParameters() {
- Pattern p = Pattern.compile(namePrefix + "(?<paramName>.+?)" + nameSuffix);
- Matcher m = p.matcher(updatedQuery);
- int lastPosition = 0;
- while (m.find(lastPosition)) {
- /**
- * We have not parsed and understood the SQL query; the parameter-like looking string
- * could be inside a literal part of the query → just warning, not exception.
- */
- log.log(Level.WARNING, "Possibly missing parameter „{0}“ in the query: „{1}“", new Object[]{m.group("paramName"), getQuery()});
- lastPosition = m.end();
- }
- }
-
- @Override
- public List<NamedParameter> getParameters() {
- return parameters;
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/SQLCommandNumbered.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk;
-
-import static info.globalcode.sql.dk.Functions.notNull;
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-import java.util.List;
-
-/**
- * Has ordinal/numbered parameters.
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class SQLCommandNumbered extends SQLCommand {
-
- private List<? extends Parameter> parameters;
-
- public SQLCommandNumbered(String query, List<? extends Parameter> parameters) {
- super(query);
- this.parameters = parameters;
- }
-
- @Override
- public void parametrize(PreparedStatement ps) throws SQLException {
- int i = 1;
- for (Parameter p : notNull(parameters)) {
- ps.setObject(i++, p.getValue(), p.getType().getCode());
- }
- }
-
- @Override
- public List<? extends Parameter> getParameters() {
- return parameters;
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/SQLType.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,95 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk;
-
-import java.sql.Types;
-
-/**
- * Data types of SQL parameters.
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public enum SQLType {
-
- /**
- * Names must be upper case – user input is also converted to upper case → case insensitive
- */
- BIT(Types.BIT),
- TINYINT(Types.TINYINT),
- SMALLINT(Types.SMALLINT),
- INTEGER(Types.INTEGER),
- BIGINT(Types.BIGINT),
- FLOAT(Types.FLOAT),
- REAL(Types.REAL),
- DOUBLE(Types.DOUBLE),
- NUMERIC(Types.NUMERIC),
- DECIMAL(Types.DECIMAL),
- CHAR(Types.CHAR),
- VARCHAR(Types.VARCHAR),
- LONGVARCHAR(Types.LONGVARCHAR),
- DATE(Types.DATE),
- TIME(Types.TIME),
- TIMESTAMP(Types.TIMESTAMP),
- BINARY(Types.BINARY),
- VARBINARY(Types.VARBINARY),
- LONGVARBINARY(Types.LONGVARBINARY),
- NULL(Types.NULL),
- OTHER(Types.OTHER),
- JAVA_OBJECT(Types.JAVA_OBJECT),
- DISTINCT(Types.DISTINCT),
- STRUCT(Types.STRUCT),
- ARRAY(Types.ARRAY),
- BLOB(Types.BLOB),
- CLOB(Types.CLOB),
- REF(Types.REF),
- DATALINK(Types.DATALINK),
- BOOLEAN(Types.BOOLEAN),
- ROWID(Types.ROWID),
- NCHAR(Types.NCHAR),
- NVARCHAR(Types.NVARCHAR),
- LONGNVARCHAR(Types.LONGNVARCHAR),
- NCLOB(Types.NCLOB),
- SQLXML(Types.SQLXML);
- /** value from java.sql.Types */
- private int code;
-
- private SQLType(int code) {
- this.code = code;
- }
-
- /**
- * @see java.sql.Types.Types
- */
- public int getCode() {
- return code;
- }
-
- /**
- * @param code see {@linkplain java.sql.Types.Types}
- * @return found SQLType
- * @throws IllegalArgumentException if no data type has given code
- */
- public static SQLType valueOf(int code) {
- for (SQLType t : values()) {
- if (t.code == code) {
- return t;
- }
- }
- throw new IllegalArgumentException("No data type has code: " + code);
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/Xmlns.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk;
-
-/**
- * XML namespaces
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class Xmlns {
-
- public static final String CONFIGURATION = "https://sql-dk.globalcode.info/xmlns/configuration";
- public static final String BATCH_RESULT = "https://sql-dk.globalcode.info/xmlns/batchResult";
- public static final String XHTML = "http://www.w3.org/1999/xhtml";
-
- private Xmlns() {
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/batch/Batch.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.batch;
-
-import info.globalcode.sql.dk.SQLCommand;
-
-/**
- * Iterator which reads SQL commands from encoded (serialized) batch.
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public interface Batch {
-
- public boolean hasNext() throws BatchException;
-
- public SQLCommand next() throws BatchException;
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/batch/BatchConstants.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2014 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.batch;
-
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-
-/**
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class BatchConstants {
-
- public static final Charset CHARSET = StandardCharsets.UTF_8;
- public static final byte VERSION = 0x01;
- public static final byte[] BATCH_HEADER = {0x00, 0x53, 0x51, 0x4C, VERSION};
-
- private BatchConstants() {
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/batch/BatchDecoder.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,108 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2014 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.batch;
-
-import info.globalcode.sql.dk.Parameter;
-import info.globalcode.sql.dk.SQLCommand;
-import info.globalcode.sql.dk.SQLCommandNumbered;
-import java.io.DataInputStream;
-import java.io.InputStream;
-import static info.globalcode.sql.dk.batch.BatchConstants.*;
-import static info.globalcode.sql.dk.Functions.toHex;
-import info.globalcode.sql.dk.SQLType;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class BatchDecoder {
-
- public Batch decode(InputStream in) throws BatchException {
- return new BatchFromStream(new DataInputStream(in));
- }
-
- private class BatchFromStream implements Batch {
-
- private DataInputStream in;
- private boolean hasNext;
-
- public BatchFromStream(DataInputStream in) throws BatchException {
- this.in = in;
- hasNext = verifyHeader();
- }
-
- @Override
- public boolean hasNext() throws BatchException {
- return hasNext;
- }
-
- @Override
- public SQLCommand next() throws BatchException {
- try {
- String sql = readNextString();
-
- int paramCount = in.readInt();
- List<Parameter> parameters = new ArrayList<>(paramCount);
-
- for (int i = 0; i < paramCount; i++) {
- SQLType type = SQLType.valueOf(in.readInt());
- String value = readNextString();
- parameters.add(new Parameter(value, type));
- }
-
- hasNext = verifyHeader();
-
- SQLCommand sqlCommand = new SQLCommandNumbered(sql, parameters);
- return sqlCommand;
- } catch (IOException e) {
- throw new BatchException("Unable to read batch", e);
- }
- }
-
- private String readNextString() throws IOException {
- byte[] buffer = new byte[in.readInt()];
- in.read(buffer);
- return new String(buffer, CHARSET);
- }
-
- /**
- * @return true if correct batch header was found | false if EOF was found
- * @throws BatchException if unexpected data was found (not batch header nor EOF)
- */
- private boolean verifyHeader() throws BatchException {
- try {
- byte[] buffer = new byte[BATCH_HEADER.length];
- int bytesRead = in.read(buffer);
-
- if (bytesRead == BATCH_HEADER.length && Arrays.equals(buffer, BATCH_HEADER)) {
- return true;
- } else if (bytesRead == -1) {
- return false;
- } else {
- throw new BatchException("This is not SQL-DK batch: " + toHex(buffer));
- }
- } catch (IOException e) {
- throw new BatchException("Unable to read batch header", e);
- }
- }
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/batch/BatchEncoder.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,83 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2014 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.batch;
-
-import info.globalcode.sql.dk.Parameter;
-import info.globalcode.sql.dk.SQLCommand;
-import info.globalcode.sql.dk.SQLCommandNamed;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import static info.globalcode.sql.dk.batch.BatchConstants.*;
-import java.io.ByteArrayOutputStream;
-import java.sql.SQLException;
-import java.util.List;
-
-/**
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class BatchEncoder {
-
- public int encode(SQLCommand sqlCommand, OutputStream out) throws BatchException {
- try {
- ByteArrayOutputStream bufferAOS = new ByteArrayOutputStream();
- DataOutputStream buffer = new DataOutputStream(bufferAOS);
-
- buffer.write(BATCH_HEADER);
-
- if (sqlCommand instanceof SQLCommandNamed) {
- sqlCommand = ((SQLCommandNamed) sqlCommand).getSQLCommandNumbered();
- }
-
- writeNextString(sqlCommand.getQuery(), buffer);
-
- List<? extends Parameter> parameters = sqlCommand.getParameters();
-
- buffer.writeInt(parameters.size());
-
- for (Parameter p : parameters) {
- buffer.writeInt(p.getType().getCode());
- writeNextString((String) p.getValue(), buffer); // parameters are encoded before any preprocessing
- }
-
- buffer.flush();
- bufferAOS.writeTo(out);
- out.flush();
- return bufferAOS.size();
- } catch (IOException e) {
- throw new BatchException("Unable to write SQL command: " + sqlCommand, e);
- } catch (SQLException e) {
- throw new BatchException("Unable to converd named SQL command to numbered: " + sqlCommand, e);
- }
- }
-
- private void writeNextString(String s, DataOutputStream out) throws IOException {
- byte[] bytes = toBytes(s);
- out.writeInt(bytes.length);
- out.write(bytes);
- }
-
- private static byte[] toBytes(String s) {
- if (s == null) {
- return new byte[]{};
- } else {
- return s.getBytes(CHARSET);
- }
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/batch/BatchException.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2014 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.batch;
-
-import info.globalcode.sql.dk.DKException;
-
-/**
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class BatchException extends DKException {
-
- public BatchException() {
- }
-
- public BatchException(String message) {
- super(message);
- }
-
- public BatchException(Throwable cause) {
- super(cause);
- }
-
- public BatchException(String message, Throwable cause) {
- super(message, cause);
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/configuration/CommandArgument.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,82 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2015 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.configuration;
-
-import javax.xml.bind.annotation.XmlAttribute;
-import javax.xml.bind.annotation.XmlEnum;
-import javax.xml.bind.annotation.XmlEnumValue;
-import javax.xml.bind.annotation.XmlValue;
-
-/**
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class CommandArgument {
-
- private String value;
- private TYPE type;
-
- @XmlEnum
- public static enum TYPE {
-
- /**
- * value = literal (text) argument
- */
- @XmlEnumValue("literal")
- LITERAL,
- /**
- * value will be substituted by hostname or IP address of the DB server
- */
- @XmlEnumValue("host")
- HOST,
- /**
- * value will be substituted by the port of the DB server
- */
- @XmlEnumValue("port")
- PORT,
- /**
- * value will be substituted by environmental variable of given name
- */
- @XmlEnumValue("env")
- ENVIRONMENT_VARIABLE,
- /**
- * value will be substituted by database property of given name
- */
- @XmlEnumValue("dbProperty")
- DB_PROPERTY;
- }
-
- @XmlValue
- public String getValue() {
- return value;
- }
-
- public void setValue(String value) {
- this.value = value;
- }
-
- @XmlAttribute(name = "type")
- public TYPE getType() {
- return type;
- }
-
- public void setType(TYPE type) {
- this.type = type;
- }
-
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/configuration/Configuration.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,173 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.configuration;
-
-import static info.globalcode.sql.dk.Xmlns.CONFIGURATION;
-import static info.globalcode.sql.dk.Functions.findByName;
-import info.globalcode.sql.dk.formatting.BarChartFormatter;
-import info.globalcode.sql.dk.formatting.SilentFormatter;
-import info.globalcode.sql.dk.formatting.SingleRecordFormatter;
-import info.globalcode.sql.dk.formatting.SingleValueFormatter;
-import info.globalcode.sql.dk.formatting.TabularFormatter;
-import info.globalcode.sql.dk.formatting.TabularPrefetchingFormatter;
-import info.globalcode.sql.dk.formatting.TabularWrappingFormatter;
-import info.globalcode.sql.dk.formatting.TeXFormatter;
-import info.globalcode.sql.dk.formatting.XhtmlFormatter;
-import info.globalcode.sql.dk.formatting.XmlFormatter;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlRootElement;
-import javax.xml.bind.annotation.XmlTransient;
-
-/**
- * Object representation of user configuration loaded from XML.
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-@XmlRootElement(name = "configuration", namespace = CONFIGURATION)
-public class Configuration {
-
- private List<DatabaseDefinition> databases = new ArrayList<>();
- private List<FormatterDefinition> formatters = new ArrayList<>();
- /**
- * is used if no formatter is specified on CLI nor in user configuration
- */
- public static final String DEFAULT_FORMATTER = TabularFormatter.NAME;
- /**
- * Can be used as default if prefetching is ok – for configuration listings (config is alread in
- * memory, so this does not matter)
- */
- public static final String DEFAULT_FORMATTER_PREFETCHING = TabularPrefetchingFormatter.NAME;
- private String defaultFormatter;
- /**
- * Default list of formatters. Is used if particular name is not found in user configuration.
- */
- private static final Collection<FormatterDefinition> buildInFormatters;
-
- static {
- Collection<FormatterDefinition> l = new ArrayList<>();
- l.add(new FormatterDefinition(SilentFormatter.NAME, SilentFormatter.class.getName()));
- l.add(new FormatterDefinition(SingleValueFormatter.NAME, SingleValueFormatter.class.getName()));
- l.add(new FormatterDefinition(SingleRecordFormatter.NAME, SingleRecordFormatter.class.getName()));
- l.add(new FormatterDefinition(XmlFormatter.NAME, XmlFormatter.class.getName()));
- l.add(new FormatterDefinition(XhtmlFormatter.NAME, XhtmlFormatter.class.getName()));
- l.add(new FormatterDefinition(TabularFormatter.NAME, TabularFormatter.class.getName()));
- l.add(new FormatterDefinition(TabularPrefetchingFormatter.NAME, TabularPrefetchingFormatter.class.getName()));
- l.add(new FormatterDefinition(TabularWrappingFormatter.NAME, TabularWrappingFormatter.class.getName()));
- l.add(new FormatterDefinition(TeXFormatter.NAME, TeXFormatter.class.getName()));
- //l.add(new FormatterDefinition(DsvFormatter.NAME, DsvFormatter.class.getName()));
- //l.add(new FormatterDefinition(SystemCommandExecutor.NAME, SystemCommandExecutor.class.getName()));
- l.add(new FormatterDefinition(BarChartFormatter.NAME, BarChartFormatter.class.getName()));
- buildInFormatters = Collections.unmodifiableCollection(l);
- }
-
- @XmlElement(name = "database", namespace = CONFIGURATION)
- public List<DatabaseDefinition> getDatabases() {
- return databases;
- }
-
- public void setDatabases(List<DatabaseDefinition> databases) {
- this.databases = databases;
- }
-
- /**
- * @param name
- * @return
- * @throws ConfigurationException if no database with this name is configured
- */
- public DatabaseDefinition getDatabase(String name) throws ConfigurationException {
- DatabaseDefinition dd = findByName(databases, name);
- if (dd == null) {
- throw new ConfigurationException("Database is not configured: " + name);
- } else {
- return dd;
- }
- }
-
- /**
- * @return only configured formatters
- * @see #getBuildInFormatters()
- * @see #getAllFormatters()
- */
- @XmlElement(name = "formatter", namespace = CONFIGURATION)
- public List<FormatterDefinition> getFormatters() {
- return formatters;
- }
-
- public void setFormatters(List<FormatterDefinition> formatters) {
- this.formatters = formatters;
- }
-
- /**
- * @param name name of desired formatter. Looking for this name in user configuration, then in
- * buil-in formatters. If null, default from configuration or (if not configured) built-in
- * default is used.
- * @return formatter definition
- * @throws ConfigurationException if no formatter with this name was found
- */
- public FormatterDefinition getFormatter(String name) throws ConfigurationException {
- if (name == null) {
- return defaultFormatter == null ? getFormatter(DEFAULT_FORMATTER) : getFormatter(defaultFormatter);
- } else {
- FormatterDefinition fd = findByName(formatters, name);
- fd = fd == null ? findByName(buildInFormatters, name) : fd;
- if (fd == null) {
- throw new ConfigurationException("Formatter is not configured: " + name);
- } else {
- return fd;
- }
- }
- }
-
- /**
- * @return only built-in formatters
- * @see #getAllFormatters()
- * @see #getFormatters()
- */
- @XmlTransient
- public Collection<FormatterDefinition> getBuildInFormatters() {
- return buildInFormatters;
- }
-
- /**
- * @return built-in + configured formatters
- * @see #getFormatters()
- */
- @XmlTransient
- public Collection<FormatterDefinition> getAllFormatters() {
- Collection<FormatterDefinition> allFormatters = new ArrayList<>();
- allFormatters.addAll(buildInFormatters);
- allFormatters.addAll(formatters);
- return allFormatters;
- }
-
- /**
- * @return name of default formatter, is used if name is not specified on CLI
- */
- @XmlElement(name = "defaultFormatter", namespace = CONFIGURATION)
- public String getDefaultFormatter() {
- return defaultFormatter;
- }
-
- public void setDefaultFormatter(String defaultFormatter) {
- this.defaultFormatter = defaultFormatter;
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/configuration/ConfigurationException.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.configuration;
-
-import info.globalcode.sql.dk.DKException;
-
-/**
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class ConfigurationException extends DKException {
-
- public ConfigurationException() {
- }
-
- public ConfigurationException(String message) {
- super(message);
- }
-
- public ConfigurationException(Throwable cause) {
- super(cause);
- }
-
- public ConfigurationException(String message, Throwable cause) {
- super(message, cause);
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/configuration/ConfigurationProvider.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,28 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.configuration;
-
-/**
- * Use for lazy-loading of the configuration.
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public interface ConfigurationProvider {
-
- public Configuration getConfiguration() throws ConfigurationException;
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/configuration/DatabaseDefinition.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,147 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.configuration;
-
-import static info.globalcode.sql.dk.Xmlns.CONFIGURATION;
-import info.globalcode.sql.dk.DatabaseConnection;
-import info.globalcode.sql.dk.jmx.ConnectionManagement;
-import java.sql.SQLException;
-import java.util.logging.Logger;
-import javax.xml.bind.annotation.XmlElement;
-
-/**
- * Configured (but not yet connected) database connection.
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class DatabaseDefinition implements NameIdentified {
-
- private static final Logger log = Logger.getLogger(DatabaseDefinition.class.getName());
- /**
- * database name in SQL-DK configuration
- */
- private String name;
- /**
- * JDBC URL
- */
- private String url;
- /**
- * JDBC user name
- */
- private String userName;
- /**
- * JDBC password
- */
- private String password;
- /**
- * optional JDBC driver – if empty, the DriverManager is used to lookup specific Driver for
- * given URL
- */
- private String driver;
- /**
- * JDBC properties
- */
- private Properties properties = new Properties();
- /**
- * optional definition of tunnel to the remote database
- */
- private TunnelDefinition tunnel;
-
- @XmlElement(name = "name", namespace = CONFIGURATION)
- @Override
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- @XmlElement(name = "url", namespace = CONFIGURATION)
- public String getUrl() {
- return url;
- }
-
- public void setUrl(String url) {
- this.url = url;
- }
-
- @XmlElement(name = "userName", namespace = CONFIGURATION)
- public String getUserName() {
- return userName;
- }
-
- public void setUserName(String userName) {
- this.userName = userName;
- }
-
- @XmlElement(name = "password", namespace = CONFIGURATION)
- public String getPassword() {
- return password;
- }
-
- public void setPassword(String password) {
- this.password = password;
- }
-
- public String getDriver() {
- return driver;
- }
-
- public void setDriver(String driver) {
- this.driver = driver;
- }
-
- @XmlElement(name = "property", namespace = CONFIGURATION)
- public Properties getProperties() {
- return properties;
- }
-
- public void setProperties(Properties properties) {
- this.properties = properties;
- }
-
- public TunnelDefinition getTunnel() {
- return tunnel;
- }
-
- public void setTunnel(TunnelDefinition tunnel) {
- this.tunnel = tunnel;
- }
-
- /**
- * @param properties ad-hoc properties from CLI options (for the JDBC driver)
- * @param jmxBean JMX management bean for progress reporting | null = disable JMX
- * @return
- * @throws java.sql.SQLException
- */
- public DatabaseConnection connect(Properties properties, ConnectionManagement jmxBean) throws SQLException {
- return new DatabaseConnection(this, properties, jmxBean);
- }
-
- /**
- * @param properties
- * @return
- * @throws java.sql.SQLException
- * @see #connect(info.globalcode.sql.dk.configuration.Properties, java.lang.String)
- * With disabled JMX reporting.
- */
- public DatabaseConnection connect(Properties properties) throws SQLException {
- return new DatabaseConnection(this, properties, null);
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/configuration/FormatterDefinition.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,114 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.configuration;
-
-import static info.globalcode.sql.dk.Xmlns.CONFIGURATION;
-import info.globalcode.sql.dk.formatting.Formatter;
-import info.globalcode.sql.dk.formatting.FormatterContext;
-import info.globalcode.sql.dk.formatting.FormatterException;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import javax.xml.bind.annotation.XmlElement;
-
-/**
- * Configured (but not yet instantiated) formatter.
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class FormatterDefinition implements NameIdentified {
-
- private String name;
- private String className;
- private Properties properties = new Properties();
-
- public FormatterDefinition() {
- }
-
- public FormatterDefinition(String name, String className) {
- this.name = name;
- this.className = className;
- }
-
- public FormatterDefinition(String name, String className, Properties properties) {
- this(name, className);
- this.properties = properties;
- }
-
- @XmlElement(name = "name", namespace = CONFIGURATION)
- @Override
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- /**
- * Filter's class. Must implement the
- * <code>info.globalcode.sql.dk.formatting.Formatter</code> interface.
- * Subclassing the
- * <code>info.globalcode.sql.dk.formatting.AbstractFormatter</code> is strongly recommended.
- * The constructor must accept one parameter:
- * <code>info.globalcode.sql.dk.formatting.FormatterContext</code>
- *
- * @return fully qualified class name
- */
- @XmlElement(name = "class", namespace = CONFIGURATION)
- public String getClassName() {
- return className;
- }
-
- public void setClassName(String className) {
- this.className = className;
- }
-
- @XmlElement(name = "property", namespace = CONFIGURATION)
- public Properties getProperties() {
- return properties;
- }
-
- public void setProperties(Properties properties) {
- this.properties = properties;
- }
-
- /**
- * @param context
- * @return
- * @throws FormatterException
- */
- public Formatter getInstance(FormatterContext context) throws FormatterException {
- context.getProperties().setDefaults(properties);
- try {
- Constructor constructor = Class.forName(className).getConstructor(context.getClass());
-
- Object instance = constructor.newInstance(context);
- if (instance instanceof Formatter) {
- return (Formatter) instance;
- } else {
- throw new FormatterException("Formatter " + instance + " does not implement the " + Formatter.class.getName() + " interface");
- }
- } catch (ClassNotFoundException e) {
- throw new FormatterException("Formatter class does not exist: " + className, e);
- } catch (NoSuchMethodException e) {
- throw new FormatterException("Formatter class with no valid constructor: " + className, e);
- } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
- throw new FormatterException("Formatter's constructor caused an error: " + className, e);
- }
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/configuration/Loader.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,101 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2015 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.configuration;
-
-import info.globalcode.sql.dk.*;
-import static info.globalcode.sql.dk.DatabaseConnection.JDBC_PROPERTY_USER;
-import static info.globalcode.sql.dk.DatabaseConnection.JDBC_PROPERTY_PASSWORD;
-import java.sql.Connection;
-import java.sql.Driver;
-import java.sql.DriverManager;
-import java.sql.SQLException;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import javax.xml.bind.JAXBContext;
-import javax.xml.bind.Unmarshaller;
-
-/**
- * Configuration loader – deserializes Configuration from the XML file.
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class Loader {
-
- private static final Logger log = Logger.getLogger(Loader.class.getName());
-
- public Configuration loadConfiguration() throws ConfigurationException {
- try {
- JAXBContext jaxb = JAXBContext.newInstance(Configuration.class.getPackage().getName(), Configuration.class.getClassLoader());
- Unmarshaller u = jaxb.createUnmarshaller();
- return (Configuration) u.unmarshal(Constants.CONFIG_FILE);
- } catch (Exception e) {
- throw new ConfigurationException("Unable to load configuration from " + Constants.CONFIG_FILE, e);
- }
- }
-
- /**
- * JDBC connection should not be used directly in SQL-DK.
- *
- * @see DatabaseDefinition#connect(info.globalcode.sql.dk.configuration.Properties)
- * @param properties
- * @param databaseDefinition
- * @return
- * @throws java.sql.SQLException
- */
- public static Connection jdbcConnect(DatabaseDefinition databaseDefinition, Properties properties) throws SQLException {
- synchronized (properties) {
- /**
- * Avoid rewriting the properties. Usually, the connection is created only once, but
- * with --test-connection and with SQL-DK JDBC driver, it might be reused.
- */
- properties = properties.clone();
- }
- if (properties.hasProperty(JDBC_PROPERTY_PASSWORD)) {
- log.log(Level.WARNING, "Passing DB password as CLI parameter is insecure!");
- }
- Properties credentials = new Properties();
- credentials.add(new Property(JDBC_PROPERTY_USER, databaseDefinition.getUserName()));
- credentials.add(new Property(JDBC_PROPERTY_PASSWORD, databaseDefinition.getPassword()));
- credentials.setDefaults(databaseDefinition.getProperties());
- properties.setDefaults(credentials);
- java.util.Properties javaProperties = properties.getJavaProperties();
-
- String driverClassName = databaseDefinition.getDriver();
- final String url = databaseDefinition.getUrl();
- if (driverClassName == null) {
- log.log(Level.FINE, "Using DriverManager to create connection for „{0}“", url);
- return DriverManager.getConnection(url, javaProperties);
- } else {
- log.log(Level.FINE, "Using custom Driver „{0}“ to create connection for „{1}“", new Object[]{driverClassName, url});
- try {
- Class<Driver> driverClass = (Class<Driver>) Class.forName(driverClassName);
- Driver driver = driverClass.newInstance();
- Connection connection = driver.connect(url, javaProperties);
- if (connection == null) {
- log.log(Level.SEVERE, "Driver „{0}“ returend null → it does not accept the URL: „{1}“", new Object[]{driverClassName, url});
- throw new SQLException("Unable to connect: driver returned null.");
- } else {
- return connection;
- }
- } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | SQLException e) {
- throw new SQLException("Unable to connect usig specific driver: " + driverClassName, e);
- }
- }
- }
-
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/configuration/NameIdentified.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.configuration;
-
-/**
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public interface NameIdentified {
-
- public String getName();
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/configuration/Properties.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,129 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.configuration;
-
-import java.util.ArrayList;
-import javax.xml.bind.annotation.XmlTransient;
-import static info.globalcode.sql.dk.Functions.findByName;
-import java.util.Collections;
-
-/**
- * <p>
- * List of configurables.</p>
- *
- * <p>
- * Can be backed by defaults – if value for given name is nof found in this instance, we will
- * look into defaults. Methods also accept defaultValue parameter – is used if property is nof found
- * even in default properties.</p>
- *
- * <p>
- * Typical use: </p>
- * <ul>
- * <li>this instance – ad-hoc properties from CLI options</li>
- * <li>default properties – from config file</li>
- * <li>defaultValue – hardcoded default</li>
- * </ul>
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class Properties extends ArrayList<Property> implements Cloneable {
-
- private Properties defaults;
-
- public Properties() {
- }
-
- public Properties(int initialCapacity) {
- super(initialCapacity);
- }
-
- @XmlTransient
- public Properties getDefaults() {
- return defaults;
- }
-
- public void setDefaults(Properties defaults) {
- this.defaults = defaults;
- }
-
- /**
- * @param defaults the last/deepest defaults
- */
- public void setLastDefaults(Properties defaults) {
- if (this.defaults == null) {
- this.defaults = defaults;
- } else {
- this.defaults.setLastDefaults(defaults);
- }
- }
-
- private Property findProperty(String name) {
- Property p = findByName(this, name);
- if (p == null && defaults != null) {
- p = defaults.findProperty(name);
- }
- return p;
- }
-
- public String getString(String name, String defaultValue) {
- Property p = findProperty(name);
- return p == null ? defaultValue : p.getValue();
- }
-
- public boolean getBoolean(String name, boolean defaultValue) {
- Property p = findProperty(name);
- return p == null ? defaultValue : Boolean.valueOf(p.getValue());
- }
-
- public int getInteger(String name, int defaultValue) {
- Property p = findProperty(name);
- return p == null ? defaultValue : Integer.valueOf(p.getValue());
- }
-
- public boolean hasProperty(String name) {
- return findByName(this, name) != null;
- }
-
- @Override
- public Properties clone() {
- Properties clone = new Properties(size());
- Collections.copy(clone, this);
- return clone;
- }
-
- /**
- * @return merged this and backing defaults as Java Properties
- */
- public java.util.Properties getJavaProperties() {
- java.util.Properties javaProperties = new java.util.Properties();
- duplicateTo(javaProperties);
- return javaProperties;
- }
-
- private void duplicateTo(java.util.Properties javaProperties) {
- if (defaults != null) {
- defaults.duplicateTo(javaProperties);
- }
- for (Property p : this) {
- String value = p.getValue();
- if (value != null) {
- javaProperties.setProperty(p.getName(), value);
- }
- }
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/configuration/Property.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.configuration;
-
-import javax.xml.bind.annotation.XmlAttribute;
-import javax.xml.bind.annotation.XmlValue;
-
-/**
- * One configurable
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class Property implements NameIdentified, Cloneable {
-
- private String name;
- private String value;
-
- public Property() {
- }
-
- public Property(String name, String value) {
- this.name = name;
- this.value = value;
- }
-
- @XmlAttribute(name = "name")
- @Override
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- @XmlValue
- public String getValue() {
- return value;
- }
-
- public void setValue(String value) {
- this.value = value;
- }
-
- @Override
- public String toString() {
- return name + "='" + value + "'";
- }
-
- @Override
- protected Property clone() {
- return new Property(name, value);
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/configuration/PropertyDeclaration.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2015 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.configuration;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Repeatable;
-import java.lang.annotation.Retention;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-import java.lang.annotation.Target;
-
-/**
- * Declaration of the (formatter) properties – for documentation purposes.
- *
- * TODO: automatically inject properties (configured, ad-hoc, default ones) to the formatters
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-@Retention(RUNTIME)
-@Target({ElementType.TYPE})
-@Repeatable(PropertyDeclarations.class)
-public @interface PropertyDeclaration {
-
- /**
- * @return name of the property
- */
- String name();
-
- /**
- * @return data type of the value: String, numbers, Boolean or Enum
- */
- Class type();
-
- /**
- * @return documentation for the users
- */
- String description();
-
- /**
- * @return default value of this property
- */
- String defaultValue();
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/configuration/PropertyDeclarations.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2015 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.configuration;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-import java.lang.annotation.Target;
-
-/**
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-@Retention(RUNTIME)
-@Target({ElementType.TYPE})
-public @interface PropertyDeclarations {
-
- PropertyDeclaration[] value();
-
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/configuration/TunnelDefinition.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2015 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.configuration;
-
-import static info.globalcode.sql.dk.Xmlns.CONFIGURATION;
-import java.util.List;
-import javax.xml.bind.annotation.XmlElement;
-
-/**
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class TunnelDefinition {
-
- private String command;
- private List<CommandArgument> arguments;
-
- @XmlElement(name = "command", namespace = CONFIGURATION)
- public String getCommand() {
- return command;
- }
-
- public void setCommand(String command) {
- this.command = command;
- }
-
- @XmlElement(name = "argument", namespace = CONFIGURATION)
- public List<CommandArgument> getArguments() {
- return arguments;
- }
-
- public void setArguments(List<CommandArgument> arguments) {
- this.arguments = arguments;
- }
-
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/configuration/jaxb.index Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-Configuration
\ No newline at end of file
--- a/java/sql-dk/src/info/globalcode/sql/dk/formatting/AbstractFormatter.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,254 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.formatting;
-
-import info.globalcode.sql.dk.Parameter;
-import info.globalcode.sql.dk.configuration.DatabaseDefinition;
-import java.util.EmptyStackException;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Stack;
-
-/**
- * <ol>
- * <li>ensures integrity – if methods are called in correct order and context</li>
- * <li>provides default implmentations of methods that does not produce any output for given
- * events</li>
- * </ol>
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public abstract class AbstractFormatter implements Formatter {
-
- private Stack<State> state = new Stack<>();
- private FormatterContext formatterContext;
- private ColumnsHeader currentColumnsHeader;
- private String currentQuery;
- private int currentColumnsCount;
- private int currentRowCount;
-
- public AbstractFormatter(FormatterContext formatterContext) {
- this.formatterContext = formatterContext;
- state.push(State.ROOT);
- }
-
- /*
- * root
- * .batch
- * ..database
- * ...statement
- * ....@query
- * ....@parameters
- * ....resultSet
- * .....row
- * ......@columnValue
- * ....@updatesResult
- */
- protected enum State {
-
- ROOT,
- BATCH,
- DATABASE,
- STATEMENT,
- RESULT_SET,
- ROW
- }
-
- /**
- * Go down in hierarchy.
- * Pushes new state and verifies the old one.
- *
- * @param current the new state – currently entering
- * @param expected expected previous states (any of them is valid)
- * @return previous state
- * @throws IllegalStateException if previous state was not one from expected
- */
- private State pushState(State current, EnumSet expected) {
- State previous = state.peek();
-
- if (expected.contains(previous)) {
- state.push(current);
- return previous;
- } else {
- throw new IllegalStateException("Formatter was in wrong state: " + previous + " when it should be in one of: " + expected);
- }
- }
-
- protected State peekState(EnumSet expected) {
- State current = state.peek();
-
- if (expected.contains(current)) {
- return current;
- } else {
- throw new IllegalStateException("Formatter is in wrong state: " + current + " when it should be in one of: " + expected);
- }
-
- }
-
- /**
- * Go up in hierarchy.
- * Pops the superior state/branch.
- *
- * @param expected expected superior state
- * @return the superior state
- * @throws IllegalStateException if superior state was not one from expected or if there is no
- * more superior state (we are at root level)
- */
- private State popState(EnumSet expected) {
- try {
- state.pop();
- State superior = state.peek();
- if (expected.contains(superior)) {
- return superior;
- } else {
- throw new IllegalStateException("Formatter had wrong superior state: " + superior + " when it should be in one of: " + expected);
- }
- } catch (EmptyStackException e) {
- throw new IllegalStateException("Formatter was already at root level – there is nothing above that.", e);
- }
- }
-
- @Override
- public void writeStartBatch() {
- pushState(State.BATCH, EnumSet.of(State.ROOT));
- }
-
- @Override
- public void writeEndBatch() {
- popState(EnumSet.of(State.ROOT));
- }
-
- @Override
- public void writeStartDatabase(DatabaseDefinition databaseDefinition) {
- pushState(State.DATABASE, EnumSet.of(State.BATCH));
- }
-
- @Override
- public void writeEndDatabase() {
- popState(EnumSet.of(State.BATCH));
- }
-
- @Override
- public void writeStartStatement() {
- pushState(State.STATEMENT, EnumSet.of(State.DATABASE));
- }
-
- @Override
- public void writeEndStatement() {
- popState(EnumSet.of(State.DATABASE));
- }
-
- @Override
- public void writeStartResultSet(ColumnsHeader header) {
- pushState(State.RESULT_SET, EnumSet.of(State.STATEMENT));
- currentRowCount = 0;
- currentColumnsHeader = header;
- }
-
- @Override
- public void writeEndResultSet() {
- popState(EnumSet.of(State.STATEMENT));
- currentColumnsHeader = null;
- }
-
- @Override
- public void writeQuery(String sql) {
- peekState(EnumSet.of(State.STATEMENT));
-
- if (currentColumnsHeader == null) {
- currentQuery = sql;
- } else {
- throw new IllegalStateException("Query string '" + sql + "' must be set before columns header – was already set: " + currentColumnsHeader);
- }
- }
-
- @Override
- public void writeParameters(List<? extends Parameter> parameters) {
- peekState(EnumSet.of(State.STATEMENT));
-
- if (currentColumnsHeader != null) {
- throw new IllegalStateException("Parameters '" + parameters + "' must be set before columns header – was already set: " + currentColumnsHeader);
- }
-
- if (currentQuery == null && parameters != null) {
- throw new IllegalStateException("Parameters '" + parameters + "' must be set after query – was not yet set.");
- }
- }
-
- @Override
- public void writeStartRow() {
- pushState(State.ROW, EnumSet.of(State.RESULT_SET));
- currentColumnsCount = 0;
- currentRowCount++;
- }
-
- @Override
- public void writeEndRow() {
- popState(EnumSet.of(State.RESULT_SET));
- }
-
- @Override
- public void writeColumnValue(Object value) {
- peekState(EnumSet.of(State.ROW));
- currentColumnsCount++;
-
- int declaredCount = currentColumnsHeader.getColumnCount();
- if (currentColumnsCount > declaredCount) {
- throw new IllegalStateException("Current columns count is " + currentColumnsCount + " which is more than declared " + declaredCount + " in header.");
- }
- }
-
- @Override
- public void writeUpdatesResult(int updatedRowsCount) {
- peekState(EnumSet.of(State.STATEMENT));
- }
-
- @Override
- public void close() throws FormatterException {
- }
-
- public FormatterContext getFormatterContext() {
- return formatterContext;
- }
-
- protected ColumnsHeader getCurrentColumnsHeader() {
- return currentColumnsHeader;
- }
-
- /**
- * @return column number, 1 = first
- */
- protected int getCurrentColumnsCount() {
- return currentColumnsCount;
- }
-
- protected boolean isCurrentColumnFirst() {
- return currentColumnsCount == 1;
- }
-
- protected boolean isCurrentColumnLast() {
- return currentColumnsCount == currentColumnsHeader.getColumnCount();
- }
-
- /**
- * @return row number, 1 = first
- */
- protected int getCurrentRowCount() {
- return currentRowCount;
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/formatting/AbstractXmlFormatter.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,241 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2014 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.formatting;
-
-import info.globalcode.sql.dk.ColorfulPrintWriter;
-import info.globalcode.sql.dk.ColorfulPrintWriter.TerminalColor;
-import java.util.Stack;
-import javax.xml.namespace.QName;
-import static info.globalcode.sql.dk.Functions.isEmpty;
-import static info.globalcode.sql.dk.Functions.toHex;
-import info.globalcode.sql.dk.configuration.PropertyDeclaration;
-import static info.globalcode.sql.dk.formatting.CommonProperties.COLORFUL;
-import static info.globalcode.sql.dk.formatting.CommonProperties.COLORFUL_DESCRIPTION;
-import java.nio.charset.Charset;
-import java.util.EmptyStackException;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * <p>
- * Provides helper methods for printing pretty intended and optionally colorful (syntax highlighted)
- * XML output.
- * </p>
- *
- * <p>
- * Must be used with care – bad usage can lead to invalid XML (e.g. using undeclared namespaces).
- * </p>
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-@PropertyDeclaration(name = COLORFUL, defaultValue = "false", type = Boolean.class, description = COLORFUL_DESCRIPTION)
-@PropertyDeclaration(name = AbstractXmlFormatter.PROPERTY_INDENT, defaultValue = AbstractXmlFormatter.PROPERTY_INDENT_DEFAULT, type = String.class, description = "tab or sequence of spaces used for indentation of nested elements")
-@PropertyDeclaration(name = AbstractXmlFormatter.PROPERTY_INDENT_TEXT, defaultValue = "true", type = Boolean.class, description = "whether text with line breaks should be indented; if not original whitespace will be preserved.")
-public abstract class AbstractXmlFormatter extends AbstractFormatter {
-
- private static final Logger log = Logger.getLogger(AbstractXmlFormatter.class.getName());
- public static final String PROPERTY_INDENT = "indent";
- protected static final String PROPERTY_INDENT_DEFAULT = "\t";
- public static final String PROPERTY_INDENT_TEXT = "indentText";
- private static final TerminalColor ELEMENT_COLOR = TerminalColor.Magenta;
- private static final TerminalColor ATTRIBUTE_NAME_COLOR = TerminalColor.Green;
- private static final TerminalColor ATTRIBUTE_VALUE_COLOR = TerminalColor.Yellow;
- private static final TerminalColor XML_DECLARATION_COLOR = TerminalColor.Red;
- private static final TerminalColor XML_DOCTYPE_COLOR = TerminalColor.Cyan;
- private Stack<QName> treePosition = new Stack<>();
- private final ColorfulPrintWriter out;
- private final String indent;
- private final boolean indentText;
-
- public AbstractXmlFormatter(FormatterContext formatterContext) {
- super(formatterContext);
- boolean colorful = formatterContext.getProperties().getBoolean(COLORFUL, false);
- out = new ColorfulPrintWriter(formatterContext.getOutputStream(), false, colorful);
- indent = formatterContext.getProperties().getString(PROPERTY_INDENT, PROPERTY_INDENT_DEFAULT);
- indentText = formatterContext.getProperties().getBoolean(PROPERTY_INDENT_TEXT, true);
-
- if (!indent.matches("\\s*")) {
- log.log(Level.WARNING, "Setting indent to „{0}“ is weird & freaky; in hex: {1}", new Object[]{indent, toHex(indent.getBytes())});
- }
-
- }
-
- protected void printStartDocument() {
- out.print(XML_DECLARATION_COLOR, "<?xml version=\"1.0\" encoding=\"" + Charset.defaultCharset().name() + "\"?>");
- }
-
- protected void printDoctype(String doctype) {
- out.print(XML_DOCTYPE_COLOR, "\n<!DOCTYPE " + doctype + ">");
- }
-
- protected void printEndDocument() {
- out.println();
- out.flush();
- if (!treePosition.empty()) {
- throw new IllegalStateException("Some elements are not closed: " + treePosition);
- }
- }
-
- protected void printStartElement(QName element) {
- printStartElement(element, null);
- }
-
- protected Map<QName, String> singleAttribute(QName name, String value) {
- Map<QName, String> attributes = new HashMap<>(2);
- attributes.put(name, value);
- return attributes;
- }
-
- protected void printStartElement(QName element, Map<QName, String> attributes) {
- printStartElement(element, attributes, false);
- }
-
- /**
- * @param empty whether element should be closed <codfe>… /></code> (has no content, do not
- * call {@linkplain #printEndElement()})
- */
- private void printStartElement(QName element, Map<QName, String> attributes, boolean empty) {
- printIndent();
-
- out.print(ELEMENT_COLOR, "<" + toString(element));
-
- if (attributes != null) {
- for (Entry<QName, String> attribute : attributes.entrySet()) {
- out.print(" ");
- out.print(ATTRIBUTE_NAME_COLOR, toString(attribute.getKey()));
- out.print("=");
- out.print(ATTRIBUTE_VALUE_COLOR, '"' + escapeXmlAttribute(attribute.getValue()) + '"');
- }
- }
-
- if (empty) {
- out.print(ELEMENT_COLOR, "/>");
- } else {
- out.print(ELEMENT_COLOR, ">");
- treePosition.add(element);
- }
-
- out.flush();
- }
-
- /**
- * Prints text node wrapped in given element without indenting the text and adding line breaks
- * (useful for short texts).
- *
- * @param attributes use {@linkplain LinkedHashMap} to preserve attributes order
- */
- protected void printTextElement(QName element, Map<QName, String> attributes, String text) {
- printStartElement(element, attributes);
-
- String[] lines = text.split("\\n");
-
- if (indentText && lines.length > 1) {
- for (String line : lines) {
- printText(line, true);
- }
- printEndElement(true);
- } else {
- /*
- * line breaks at the end of the text will be eaten – if you need them, use indentText = false
- */
- if (lines.length == 1 && text.endsWith("\n")) {
- text = text.substring(0, text.length() - 1);
- }
-
- printText(text, false);
- printEndElement(false);
- }
- }
-
- protected void printEmptyElement(QName element, Map<QName, String> attributes) {
- printStartElement(element, attributes, true);
- }
-
- protected void printEndElement() {
- printEndElement(true);
- }
-
- private void printEndElement(boolean indent) {
- try {
- QName name = treePosition.pop();
-
- if (indent) {
- printIndent();
- }
-
- out.print(ELEMENT_COLOR, "</" + toString(name) + ">");
- out.flush();
-
- } catch (EmptyStackException e) {
- throw new IllegalStateException("No more elements to end.", e);
- }
- }
-
- protected void printText(String s, boolean indent) {
- if (indent) {
- printIndent();
- }
- out.print(escapeXmlText(s));
- out.flush();
- }
-
- protected void printIndent() {
- out.println();
- for (int i = 0; i < treePosition.size(); i++) {
- out.print(indent);
- }
- }
-
- protected static QName qname(String name) {
- return new QName(name);
- }
-
- protected static QName qname(String prefix, String name) {
- return new QName(null, name, prefix);
- }
-
- private String toString(QName name) {
- if (isEmpty(name.getPrefix(), true)) {
- return escapeName(name.getLocalPart());
- } else {
- return escapeName(name.getPrefix()) + ":" + escapeName(name.getLocalPart());
- }
- }
-
- private String escapeName(String s) {
- // TODO: avoid ugly values in <name name="…"/>
- return s;
- }
-
- private static String escapeXmlText(String s) {
- return s.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
- // Not needed:
- // return s.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """).replaceAll("'", "'");
- }
-
- /**
- * Expects attribute values enclosed in "quotes" not 'apostrophes'.
- */
- private static String escapeXmlAttribute(String s) {
- return s.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """);
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/formatting/BarChartFormatter.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,104 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2015 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.formatting;
-
-import info.globalcode.sql.dk.Functions;
-import info.globalcode.sql.dk.configuration.PropertyDeclaration;
-import info.globalcode.sql.dk.logging.LoggerProducer;
-import java.math.BigDecimal;
-import java.math.MathContext;
-import java.math.RoundingMode;
-import java.util.List;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * TODO: min/max values – range for case that no value is 100 %
- *
- * TODO: multiple barcharts in same table (last column is still default) + multiple resultsets
- *
- * TODO: negative values - bar starting from the middle, not always from the left
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-@PropertyDeclaration(name = BarChartFormatter.PROPERTY_PRECISION, type = Integer.class, defaultValue = BarChartFormatter.PROPERTY_PRECISION_DEFAULT, description = "number of characters representing 100 % in the bar chart")
-public class BarChartFormatter extends TabularPrefetchingFormatter {
-
- public static final String NAME = "barchart"; // bash-completion:formatter
- public static final String PROPERTY_PRECISION = "precision";
- protected static final String PROPERTY_PRECISION_DEFAULT = "100";
- private static final MathContext mathContext = MathContext.DECIMAL128;
- public static final Logger log = LoggerProducer.getLogger();
- private final BigDecimal chartPrecision;
- private final char chartFull;
- private final char chartEmpty;
-
- public BarChartFormatter(FormatterContext formatterContext) {
- super(formatterContext);
- chartPrecision = BigDecimal.valueOf(formatterContext.getProperties().getInteger(PROPERTY_PRECISION, Integer.parseInt(PROPERTY_PRECISION_DEFAULT)));
- chartFull = isAsciiNostalgia() ? '#' : '█';
- chartEmpty = isAsciiNostalgia() ? '~' : '░';
- // TODO: consider using partial blocks for more precision: https://en.wikipedia.org/wiki/Block_Elements
- }
-
- @Override
- protected void postprocessPrefetchedResultSet(ColumnsHeader currentHeader, List<Object[]> currentResultSet) {
- super.postprocessPrefetchedResultSet(currentHeader, currentResultSet);
-
- updateColumnWidth(currentHeader.getColumnCount(), chartPrecision.intValue());
-
- BigDecimal maximum = BigDecimal.ZERO;
- BigDecimal minimum = BigDecimal.ZERO;
- int lastIndex = currentHeader.getColumnCount() - 1;
-
- Object valueObject = null;
- try {
- for (Object[] row : currentResultSet) {
- valueObject = row[lastIndex];
- if (valueObject != null) {
- BigDecimal value = new BigDecimal(valueObject.toString());
- maximum = maximum.max(value);
- minimum = minimum.min(value);
- }
- }
-
- BigDecimal range = maximum.subtract(minimum);
-
- for (Object[] row : currentResultSet) {
- valueObject = row[lastIndex];
- if (valueObject == null) {
- row[lastIndex] = "";
- } else {
- BigDecimal value = new BigDecimal(valueObject.toString());
- BigDecimal valueFromMinimum = value.subtract(minimum);
-
- BigDecimal points = chartPrecision.divide(range, mathContext).multiply(valueFromMinimum, mathContext);
- int pointsRounded = points.setScale(0, RoundingMode.HALF_UP).intValue();
- row[lastIndex] = Functions.repeat(chartFull, pointsRounded) + Functions.repeat(chartEmpty, chartPrecision.intValue() - pointsRounded);
- }
- }
-
- } catch (NumberFormatException e) {
- // https://en.wiktionary.org/wiki/parsable
- log.log(Level.SEVERE, "Last column must be number or an object with toString() value parsable to a number. But was „{0}“", valueObject);
- // FIXME: throw FormatterException
- throw e;
- }
- }
-
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/formatting/ColumnDescriptor.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,122 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.formatting;
-
-import java.sql.Types;
-
-/**
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class ColumnDescriptor {
-
- private String name;
- private String label;
- private int type;
- private String typeName;
- private boolean firstColumn;
- private boolean lastColumn;
- private int columnNumber;
-
- /**
- * @return column name
- * @see #getLabel()
- */
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- /**
- * @return label specified by the SQL AS clause
- */
- public String getLabel() {
- return label;
- }
-
- public void setLabel(String label) {
- this.label = label;
- }
-
- public int getType() {
- return type;
- }
-
- public void setType(int type) {
- this.type = type;
- }
-
- public String getTypeName() {
- return typeName;
- }
-
- public void setTypeName(String typeName) {
- this.typeName = typeName;
- }
-
- public boolean isFirstColumn() {
- return firstColumn;
- }
-
- public void setFirstColumn(boolean firstColumn) {
- this.firstColumn = firstColumn;
- }
-
- public boolean isLastColumn() {
- return lastColumn;
- }
-
- public void setLastColumn(boolean lastColumn) {
- this.lastColumn = lastColumn;
- }
-
- /**
- * @return number of this column, 1 = first
- */
- public int getColumnNumber() {
- return columnNumber;
- }
-
- public void setColumnNumber(int columnNumber) {
- this.columnNumber = columnNumber;
- }
-
- public boolean isBoolean() {
- return type == Types.BOOLEAN;
- }
-
- public boolean isNumeric() {
- switch (type) {
- case Types.BIGINT:
- case Types.DECIMAL:
- case Types.DOUBLE:
- case Types.FLOAT:
- case Types.INTEGER:
- case Types.NUMERIC:
- case Types.REAL:
- case Types.SMALLINT:
- case Types.TINYINT:
- return true;
- default:
- return false;
- }
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/formatting/ColumnsHeader.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,70 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.formatting;
-
-import java.sql.ResultSetMetaData;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class ColumnsHeader {
-
- private ResultSetMetaData metaData;
-
- public ColumnsHeader(ResultSetMetaData metaData) {
- this.metaData = metaData;
- }
-
- public int getColumnCount() {
- try {
- return metaData.getColumnCount();
- } catch (SQLException e) {
- throw new IllegalStateException("Error during getting column count.", e);
- }
- }
-
- public List<ColumnDescriptor> getColumnDescriptors() {
- try {
- int count = metaData.getColumnCount();
- List<ColumnDescriptor> list = new ArrayList<>(count);
-
- for (int i = 1; i <= count; i++) {
- ColumnDescriptor cd = new ColumnDescriptor();
-
- cd.setFirstColumn(i == 1);
- cd.setLastColumn(i == count);
- cd.setColumnNumber(i);
-
- cd.setLabel(metaData.getColumnLabel(i));
- cd.setName(metaData.getColumnName(i));
- cd.setType(metaData.getColumnType(i));
- cd.setTypeName(metaData.getColumnTypeName(i));
- /** TODO: more properties */
- list.add(cd);
- }
-
- return list;
- } catch (SQLException e) {
- throw new IllegalStateException("Error during building column descriptors.", e);
- }
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/formatting/CommonProperties.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2015 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.formatting;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class CommonProperties {
-
- private static final Map<Class, String> TYPE_SIMPLE_NAMES;
-
- static {
- Map<Class, String> m = new HashMap<>();
- m.put(Boolean.class, "boolean");
- m.put(String.class, "String");
- m.put(Character.class, "char");
- m.put(Integer.class, "int");
- m.put(Long.class, "long");
- m.put(Double.class, "double");
- TYPE_SIMPLE_NAMES = Collections.unmodifiableMap(m);
- }
-
- public static String getSimpleTypeName(Class type) {
- String name = TYPE_SIMPLE_NAMES.get(type);
- return name == null ? type.getName() : name;
- }
-
- public static final String COLORFUL = "color";
- public static final String COLORFUL_DESCRIPTION = "whether the output should be printed in color (ANSI Escape Sequences)";
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/formatting/FakeSqlArray.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,106 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2014 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.formatting;
-
-import info.globalcode.sql.dk.SQLType;
-import java.sql.Array;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.Map;
-
-/**
- * Fake SQL array, for formatting purposes only
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class FakeSqlArray implements Array {
-
- private static final UnsupportedOperationException exception = new UnsupportedOperationException("This is just a fake SQL array.");
- private final Object[] data;
- private final SQLType baseType;
-
- public FakeSqlArray(Object[] data, SQLType baseType) {
- this.data = data;
- this.baseType = baseType;
- }
-
- @Override
- public String toString() {
- StringBuilder string = new StringBuilder();
- for (Object o : data) {
- string.append(o);
- string.append("\n");
- }
- return string.toString();
- }
-
- @Override
- public String getBaseTypeName() throws SQLException {
- return baseType.name();
- }
-
- @Override
- public int getBaseType() throws SQLException {
- return baseType.getCode();
- }
-
- @Override
- public Object getArray() throws SQLException {
- return data;
- }
-
- @Override
- public Object getArray(Map<String, Class<?>> map) throws SQLException {
- throw exception;
- }
-
- @Override
- public Object getArray(long index, int count) throws SQLException {
- throw exception;
- }
-
- @Override
- public Object getArray(long index, int count, Map<String, Class<?>> map) throws SQLException {
- throw exception;
- }
-
- @Override
- public ResultSet getResultSet() throws SQLException {
- throw exception;
- }
-
- @Override
- public ResultSet getResultSet(Map<String, Class<?>> map) throws SQLException {
- throw exception;
- }
-
- @Override
- public ResultSet getResultSet(long index, int count) throws SQLException {
- throw exception;
- }
-
- @Override
- public ResultSet getResultSet(long index, int count, Map<String, Class<?>> map) throws SQLException {
- throw exception;
- }
-
- @Override
- public void free() throws SQLException {
- throw exception;
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/formatting/Formatter.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,67 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.formatting;
-
-import info.globalcode.sql.dk.Parameter;
-import info.globalcode.sql.dk.configuration.DatabaseDefinition;
-import java.util.List;
-
-/**
- * The formatter is responsible for printing the result sets and/or updates result (count of
- * inserted/updated rows). The formatter can produce output in arbitrary format – text, some markup
- * or even binary data.
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public interface Formatter extends AutoCloseable {
-
- void writeStartBatch();
-
- void writeStartDatabase(DatabaseDefinition databaseDefinition);
-
- void writeEndDatabase();
-
- void writeStartStatement();
-
- void writeEndStatement();
-
- void writeQuery(String sql);
-
- void writeParameters(List<? extends Parameter> parameters);
-
- void writeStartResultSet(ColumnsHeader header);
-
- void writeEndResultSet();
-
- void writeStartRow();
-
- void writeColumnValue(Object value);
-
- void writeEndRow();
-
- void writeUpdatesResult(int updatedRowsCount);
-
- void writeEndBatch();
-
- /**
- * If an error occurs (e.g. lost connection during result set reading) this method will be
- * called even if there was no {@linkplain #writeEndBach()}.
- */
- @Override
- void close() throws FormatterException;
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/formatting/FormatterContext.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.formatting;
-
-import info.globalcode.sql.dk.configuration.Properties;
-import java.io.OutputStream;
-
-/**
- * To be passed from the SQL-DK core to the formatter.
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class FormatterContext {
-
- private OutputStream outputStream;
- private Properties properties;
-
- public FormatterContext(OutputStream outputStream, Properties properties) {
- this.outputStream = outputStream;
- this.properties = properties;
- }
-
- public OutputStream getOutputStream() {
- return outputStream;
- }
-
- public Properties getProperties() {
- return properties;
- }
-
- public void setProperties(Properties properties) {
- this.properties = properties;
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/formatting/FormatterException.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.formatting;
-
-import info.globalcode.sql.dk.DKException;
-
-/**
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class FormatterException extends DKException {
-
- public FormatterException() {
- }
-
- public FormatterException(String message) {
- super(message);
- }
-
- public FormatterException(Throwable cause) {
- super(cause);
- }
-
- public FormatterException(String message, Throwable cause) {
- super(message, cause);
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/formatting/SilentFormatter.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.formatting;
-
-/**
- * Does not output anything, can be used instead of
- * <code>/dev/null</code>.
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class SilentFormatter extends AbstractFormatter {
-
- public static final String NAME = "silent"; // bash-completion:formatter
-
- public SilentFormatter(FormatterContext formatterContext) {
- super(formatterContext);
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/formatting/SingleRecordFormatter.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,105 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2015 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.formatting;
-
-import info.globalcode.sql.dk.ColorfulPrintWriter;
-import info.globalcode.sql.dk.Functions;
-import info.globalcode.sql.dk.configuration.PropertyDeclaration;
-import static info.globalcode.sql.dk.formatting.CommonProperties.COLORFUL;
-import static info.globalcode.sql.dk.formatting.CommonProperties.COLORFUL_DESCRIPTION;
-
-/**
- * Formatter intended for printing one record (or few records) with many columns.
- * Prints each colum name and its value on separate line.
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-@PropertyDeclaration(name = COLORFUL, defaultValue = "true", type = Boolean.class, description = COLORFUL_DESCRIPTION)
-public class SingleRecordFormatter extends AbstractFormatter {
-
- public static final String NAME = "record"; // bash-completion:formatter
- private final ColorfulPrintWriter out;
- private boolean firstResult = true;
-
- public SingleRecordFormatter(FormatterContext formatterContext) {
- super(formatterContext);
- out = new ColorfulPrintWriter(formatterContext.getOutputStream());
- out.setColorful(formatterContext.getProperties().getBoolean(COLORFUL, true));
- }
-
- @Override
- public void writeStartResultSet(ColumnsHeader header) {
- super.writeStartResultSet(header);
- printResultSeparator();
- }
-
- @Override
- public void writeStartRow() {
- super.writeStartRow();
- printRecordSeparator();
- out.print(ColorfulPrintWriter.TerminalColor.Red, "Record: ");
- out.print(getCurrentRowCount());
- println();
- }
-
- @Override
- public void writeColumnValue(Object value) {
- super.writeColumnValue(value);
- String columnName = getCurrentColumnsHeader().getColumnDescriptors().get(getCurrentColumnsCount() - 1).getLabel();
- out.print(ColorfulPrintWriter.TerminalColor.Green, columnName + ": ");
- Functions.printValueWithWhitespaceReplaced(out, toString(value), null, ColorfulPrintWriter.TerminalColor.Red);
- println();
- }
-
- private static String toString(Object value) {
- return String.valueOf(value);
- }
-
- @Override
- public void writeUpdatesResult(int updatedRowsCount) {
- super.writeUpdatesResult(updatedRowsCount);
- printResultSeparator();
- out.print(ColorfulPrintWriter.TerminalColor.Red, "Updated records: ");
- out.println(updatedRowsCount);
- printBellAndFlush();
- }
-
- private void printBellAndFlush() {
- out.bell();
- out.flush();
- }
-
- private void println() {
- out.println();
- printBellAndFlush();
- }
-
- private void printRecordSeparator() {
- if (getCurrentRowCount() > 1) {
- println();
- }
- }
-
- private void printResultSeparator() {
- if (firstResult) {
- firstResult = false;
- } else {
- println();
- }
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/formatting/SingleValueFormatter.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.formatting;
-
-import java.io.PrintWriter;
-
-/**
- * Prints just the value without any formatting. If the result set contains multiple records or
- * columns, the values are simply concatenate without any separators. If updates result is returned,
- * the updated records count is printed.
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class SingleValueFormatter extends AbstractFormatter {
-
- public static final String NAME = "single"; // bash-completion:formatter
- private PrintWriter out;
-
- public SingleValueFormatter(FormatterContext formatterContext) {
- super(formatterContext);
- this.out = new PrintWriter(formatterContext.getOutputStream());
- }
-
- @Override
- public void writeColumnValue(Object value) {
- super.writeColumnValue(value);
- out.print(String.valueOf(value));
- out.flush();
- }
-
- @Override
- public void writeUpdatesResult(int updatedRowsCount) {
- super.writeUpdatesResult(updatedRowsCount);
- out.print(updatedRowsCount);
- out.flush();
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/formatting/TabularFormatter.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,308 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.formatting;
-
-import info.globalcode.sql.dk.ColorfulPrintWriter;
-import static info.globalcode.sql.dk.ColorfulPrintWriter.*;
-import info.globalcode.sql.dk.Functions;
-import static info.globalcode.sql.dk.Functions.lpad;
-import static info.globalcode.sql.dk.Functions.rpad;
-import static info.globalcode.sql.dk.Functions.repeat;
-import info.globalcode.sql.dk.configuration.PropertyDeclaration;
-import static info.globalcode.sql.dk.formatting.CommonProperties.COLORFUL;
-import static info.globalcode.sql.dk.formatting.CommonProperties.COLORFUL_DESCRIPTION;
-import java.sql.SQLException;
-import java.sql.SQLXML;
-import java.util.List;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * <p>
- * Prints human-readable output – tables of result sets and text messages with update counts.
- * </p>
- *
- * <p>
- * Longer values might break the table – overflow the cells – see alternative tabular formatters and
- * the {@linkplain #PROPERTY_TRIM} property.
- * </p>
- *
- * @author Ing. František Kučera (frantovo.cz)
- * @see TabularPrefetchingFormatter
- * @see TabularWrappingFormatter
- */
-@PropertyDeclaration(name = COLORFUL, defaultValue = "true", type = Boolean.class, description = COLORFUL_DESCRIPTION)
-@PropertyDeclaration(name = TabularFormatter.PROPERTY_ASCII, defaultValue = "false", type = Boolean.class, description = "whether to use ASCII table borders instead of unicode ones")
-@PropertyDeclaration(name = TabularFormatter.PROPERTY_TRIM, defaultValue = "false", type = Boolean.class, description = "whether to trim the values to fit the column width")
-@PropertyDeclaration(name = TabularFormatter.PROPERTY_HEADER_TYPE, defaultValue = "true", type = Boolean.class, description = "whether to print data types in column headers")
-public class TabularFormatter extends AbstractFormatter {
-
- private static final Logger log = Logger.getLogger(TabularFormatter.class.getName());
- public static final String NAME = "tabular"; // bash-completion:formatter
- private static final String HEADER_TYPE_PREFIX = " (";
- private static final String HEADER_TYPE_SUFFIX = ")";
- public static final String PROPERTY_ASCII = "ascii";
- public static final String PROPERTY_TRIM = "trim";
- public static final String PROPERTY_HEADER_TYPE = "headerTypes";
- protected ColorfulPrintWriter out;
- private boolean firstResult = true;
- private int[] columnWidth;
- /**
- * use ASCII borders instead of unicode ones
- */
- private final boolean asciiNostalgia;
- /**
- * Trim values if they are longer than cell size
- */
- private final boolean trimValues;
- /**
- * Print data type of each column in the header
- */
- private final boolean printHeaderTypes;
-
- public TabularFormatter(FormatterContext formatterContext) {
- super(formatterContext);
- out = new ColorfulPrintWriter(formatterContext.getOutputStream());
- asciiNostalgia = formatterContext.getProperties().getBoolean(PROPERTY_ASCII, false);
- trimValues = formatterContext.getProperties().getBoolean(PROPERTY_TRIM, false);
- printHeaderTypes = formatterContext.getProperties().getBoolean(PROPERTY_HEADER_TYPE, true);
- out.setColorful(formatterContext.getProperties().getBoolean(COLORFUL, true));
- }
-
- @Override
- public void writeStartResultSet(ColumnsHeader header) {
- super.writeStartResultSet(header);
- printResultSeparator();
-
- initColumnWidths(header.getColumnCount());
-
- printTableIndent();
- printTableBorder("╭");
-
- List<ColumnDescriptor> columnDescriptors = header.getColumnDescriptors();
-
- for (ColumnDescriptor cd : columnDescriptors) {
- // padding: make header cell at least same width as data cells in this column
- int typeWidth = printHeaderTypes ? cd.getTypeName().length() + HEADER_TYPE_PREFIX.length() + HEADER_TYPE_SUFFIX.length() : 0;
- cd.setLabel(rpad(cd.getLabel(), getColumnWidth(cd.getColumnNumber()) - typeWidth));
- updateColumnWidth(cd.getColumnNumber(), cd.getLabel().length() + typeWidth);
-
- if (!cd.isFirstColumn()) {
- printTableBorder("┬");
- }
- printTableBorder(repeat('─', getColumnWidth(cd.getColumnNumber()) + 2));
- }
- printTableBorder("╮");
- out.println();
-
- for (ColumnDescriptor cd : columnDescriptors) {
- if (cd.isFirstColumn()) {
- printTableIndent();
- printTableBorder("│ ");
- } else {
- printTableBorder(" │ ");
- }
- out.print(TerminalStyle.Bright, cd.getLabel());
- if (printHeaderTypes) {
- out.print(HEADER_TYPE_PREFIX);
- out.print(cd.getTypeName());
- out.print(HEADER_TYPE_SUFFIX);
- }
- if (cd.isLastColumn()) {
- printTableBorder(" │");
- }
- }
- out.println();
-
- printTableIndent();
- printTableBorder("├");
- for (int i = 1; i <= header.getColumnCount(); i++) {
- if (i > 1) {
- printTableBorder("┼");
- }
- printTableBorder(repeat('─', getColumnWidth(i) + 2));
- }
- printTableBorder("┤");
- out.println();
-
- out.flush();
- }
-
- /**
- * Must be called before {@linkplain #updateColumnWidth(int, int)} and
- * {@linkplain #getColumnWidth(int)} for each result set.
- *
- * @param columnCount number of columns in current result set
- */
- protected void initColumnWidths(int columnCount) {
- if (columnWidth == null) {
- columnWidth = new int[columnCount];
- }
- }
-
- protected void cleanColumnWidths() {
- columnWidth = null;
- }
-
- @Override
- public void writeColumnValue(Object value) {
- super.writeColumnValue(value);
- writeColumnValueInternal(value);
- }
-
- protected void writeColumnValueInternal(Object value) {
-
- if (isCurrentColumnFirst()) {
- printTableIndent();
- printTableBorder("│ ");
- } else {
- printTableBorder(" │ ");
- }
-
- printValueWithWhitespaceReplaced(toString(value));
-
- if (isCurrentColumnLast()) {
- printTableBorder(" │");
- }
-
- }
-
- protected void printValueWithWhitespaceReplaced(String text) {
- Functions.printValueWithWhitespaceReplaced(out, text, TerminalColor.Cyan, TerminalColor.Red);
- }
-
- protected int getColumnWidth(int columnNumber) {
- return columnWidth[columnNumber - 1];
- }
-
- private void setColumnWidth(int columnNumber, int width) {
- columnWidth[columnNumber - 1] = width;
- }
-
- protected void updateColumnWidth(int columnNumber, int width) {
- int oldWidth = getColumnWidth(columnNumber);
- setColumnWidth(columnNumber, Math.max(width, oldWidth));
-
- }
-
- protected String toString(Object value) {
- final int width = getColumnWidth(getCurrentColumnsCount());
- String result;
- if (value instanceof Number || value instanceof Boolean) {
- result = lpad(String.valueOf(value), width);
- } else {
- if (value instanceof SQLXML) {
- // TODO: move to a common method, share with other formatters
- try {
- value = ((SQLXML) value).getString();
- } catch (SQLException e) {
- log.log(Level.SEVERE, "Unable to format XML", e);
- }
- }
-
- result = rpad(String.valueOf(value), width);
- }
- // ? value = (boolean) value ? "✔" : "✗";
-
- if (trimValues && result.length() > width) {
- result = result.substring(0, width - 1) + "…";
- }
-
- return result;
- }
-
- @Override
- public void writeEndRow() {
- super.writeEndRow();
- writeEndRowInternal();
- }
-
- public void writeEndRowInternal() {
- out.println();
- out.flush();
- }
-
- @Override
- public void writeEndResultSet() {
- int columnCount = getCurrentColumnsHeader().getColumnCount();
- super.writeEndResultSet();
-
- printTableIndent();
- printTableBorder("╰");
- for (int i = 1; i <= columnCount; i++) {
- if (i > 1) {
- printTableBorder("┴");
- }
- printTableBorder(repeat('─', getColumnWidth(i) + 2));
- }
- printTableBorder("╯");
- out.println();
-
- cleanColumnWidths();
-
- out.print(TerminalColor.Yellow, "Record count: ");
- out.println(getCurrentRowCount());
- out.bell();
- out.flush();
- }
-
- @Override
- public void writeUpdatesResult(int updatedRowsCount) {
- super.writeUpdatesResult(updatedRowsCount);
- printResultSeparator();
- out.print(TerminalColor.Red, "Updated records: ");
- out.println(updatedRowsCount);
- out.bell();
- out.flush();
- }
-
- @Override
- public void writeEndDatabase() {
- super.writeEndDatabase();
- out.flush();
- }
-
- private void printResultSeparator() {
- if (firstResult) {
- firstResult = false;
- } else {
- out.println();
- }
- }
-
- protected void printTableBorder(String border) {
- if (asciiNostalgia) {
- border = border.replaceAll("─", "-");
- border = border.replaceAll("│", "|");
- border = border.replaceAll("[╭┬╮├┼┤╰┴╯]", "+");
- }
-
- out.print(TerminalColor.Green, border);
- }
-
- protected void printTableIndent() {
- out.print(" ");
- }
-
- /**
- * @return whether should print only ASCII characters instead of unlimited Unicode.
- */
- protected boolean isAsciiNostalgia() {
- return asciiNostalgia;
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/formatting/TabularPrefetchingFormatter.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,119 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.formatting;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * <p>
- * Prefetches whole result set and computes column widths. Whole table is flushed at once in
- * {@linkplain #writeEndResultSet()}.
- * </p>
- *
- * <p>
- * Long values will not overflow the cells, but whole result set must be loaded into the memory.
- * </p>
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class TabularPrefetchingFormatter extends TabularFormatter {
-
- public static final String NAME = "tabular-prefetching"; // bash-completion:formatter
- private ColumnsHeader currentHeader;
- private List<Object[]> currentResultSet;
- private Object[] currentRow;
- private int currentColumnsCount;
- private boolean prefetchDone = false;
-
- public TabularPrefetchingFormatter(FormatterContext formatterContext) {
- super(formatterContext);
- }
-
- @Override
- protected int getCurrentColumnsCount() {
- if (prefetchDone) {
- return super.getCurrentColumnsCount();
- } else {
- return currentColumnsCount;
- }
- }
-
- @Override
- public void writeStartResultSet(ColumnsHeader header) {
- currentResultSet = new ArrayList<>();
- currentHeader = header;
- initColumnWidths(header.getColumnCount());
- }
-
- @Override
- public void writeStartRow() {
- currentRow = new Object[currentHeader.getColumnCount()];
- currentResultSet.add(currentRow);
- currentColumnsCount = 0;
- }
-
- @Override
- public void writeColumnValue(Object value) {
- currentRow[currentColumnsCount] = value;
- currentColumnsCount++;
- String textRepresentation = toString(value);
- /** TODO: count only printable characters (currently not an issue) */
- updateColumnWidth(currentColumnsCount, textRepresentation.length());
- }
-
- @Override
- public void writeEndRow() {
- // do nothing
- }
-
- @Override
- public void writeEndResultSet() {
- prefetchDone = true;
-
- postprocessPrefetchedResultSet(currentHeader, currentResultSet);
-
- super.writeStartResultSet(currentHeader);
-
- for (Object[] row : currentResultSet) {
- super.writeStartRow();
- for (Object cell : row) {
- super.writeColumnValue(cell);
- }
- super.writeEndRow();
- }
-
- currentColumnsCount = 0;
- currentHeader = null;
- currentRow = null;
- currentResultSet = null;
- super.writeEndResultSet();
- prefetchDone = false;
- }
-
- /**
- * Optional post-processing – override in sub-classes if needed.
- * Don't forget to {@linkplain #updateColumnWidth(int, int)}
- *
- * @param currentHeader
- * @param currentResultSet
- */
- protected void postprocessPrefetchedResultSet(ColumnsHeader currentHeader, List<Object[]> currentResultSet) {
- }
-
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/formatting/TabularWrappingFormatter.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,116 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2014 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.formatting;
-
-import info.globalcode.sql.dk.ColorfulPrintWriter.TerminalColor;
-import java.util.ArrayList;
-import java.util.List;
-import static info.globalcode.sql.dk.Functions.lpad;
-import static info.globalcode.sql.dk.Functions.rpad;
-import static info.globalcode.sql.dk.Functions.repeat;
-
-/**
- * Longer values are line-wrapped – the cell then contains multiple lines. Marks are added to
- * signalize forced line ends (not present in original data).
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class TabularWrappingFormatter extends TabularFormatter {
-
- public static final String NAME = "tabular-wrapping"; // bash-completion:formatter
- private List<String[]> currentRow;
-
- public TabularWrappingFormatter(FormatterContext formatterContext) {
- super(formatterContext);
- }
-
- @Override
- public void writeStartResultSet(ColumnsHeader header) {
- super.writeStartResultSet(header);
- currentRow = new ArrayList<>(header.getColumnCount());
- }
-
- @Override
- protected void writeColumnValueInternal(Object value) {
- boolean rightAlign = value instanceof Number || value instanceof Boolean;
- String valueString = String.valueOf(value);
- int columnWidth = getColumnWidth(getCurrentColumnsCount()) - 1; // -1 = space for new line symbol
- currentRow.add(wrapLines(valueString, columnWidth, rightAlign));
- }
-
- @Override
- public void writeEndRow() {
- super.writeEndRow();
-
- int wrappedLine = 0;
- boolean hasMoreWrappedLines;
-
- do {
- hasMoreWrappedLines = false;
- for (int i = 0; i < currentRow.size(); i++) {
- if (i == 0) {
- printTableIndent();
- printTableBorder("│ ");
- } else {
- printTableBorder(" │ ");
- }
- String[] columnArray = currentRow.get(i);
- if (wrappedLine < columnArray.length) {
- printValueWithWhitespaceReplaced(columnArray[wrappedLine]);
-
- if (wrappedLine < columnArray.length - 1) {
- out.print(TerminalColor.Red, "↩");
- hasMoreWrappedLines = true;
- } else {
- out.print(" ");
- }
-
- } else {
- out.print(repeat(' ', getColumnWidth(i + 1)));
- }
-
- if (i == (currentRow.size() - 1)) {
- printTableBorder(" │");
- }
- }
- out.println();
- out.flush();
- wrappedLine++;
- } while (hasMoreWrappedLines);
-
- currentRow.clear();
- }
-
- @Override
- public void writeEndRowInternal() {
- // already done – wrapped row ends
- }
-
- private static String[] wrapLines(String s, int width, boolean rightAlign) {
- String[] array = new String[(s.length() - 1) / width + 1];
- for (int i = 0; i < array.length; i++) {
- if (i == array.length - 1) {
- String part = s.substring(i * width, s.length());
- array[i] = rightAlign ? lpad(part, width) : rpad(part, width);
- } else {
- array[i] = s.substring(i * width, (i + 1) * width);
- }
- }
- return array;
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/formatting/TeXFormatter.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,208 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2014 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.formatting;
-
-import info.globalcode.sql.dk.ColorfulPrintWriter;
-import info.globalcode.sql.dk.Constants;
-import info.globalcode.sql.dk.configuration.PropertyDeclaration;
-import static info.globalcode.sql.dk.formatting.CommonProperties.COLORFUL;
-import static info.globalcode.sql.dk.formatting.CommonProperties.COLORFUL_DESCRIPTION;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Outputs result sets in (La)TeX format.
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-@PropertyDeclaration(name = COLORFUL, defaultValue = "false", type = Boolean.class, description = COLORFUL_DESCRIPTION)
-public class TeXFormatter extends AbstractFormatter {
-
- public static final String NAME = "tex"; // bash-completion:formatter
- private static final ColorfulPrintWriter.TerminalColor COMMAND_COLOR = ColorfulPrintWriter.TerminalColor.Magenta;
- private static final ColorfulPrintWriter.TerminalColor OPTIONS_COLOR = ColorfulPrintWriter.TerminalColor.Yellow;
- private static final Map<Character, String> TEX_ESCAPE_MAP;
- private final ColorfulPrintWriter out;
-
- static {
- Map<Character, String> replacements = new HashMap<>();
-
- replacements.put('\\', "\\textbackslash{}");
- replacements.put('{', "\\{{}");
- replacements.put('}', "\\}{}");
- replacements.put('_', "\\_{}");
- replacements.put('^', "\\textasciicircum{}");
- replacements.put('#', "\\#{}");
- replacements.put('&', "\\&{}");
- replacements.put('$', "\\${}");
- replacements.put('%', "\\%{}");
- replacements.put('~', "\\textasciitilde{}");
- replacements.put('-', "{-}");
-
- TEX_ESCAPE_MAP = Collections.unmodifiableMap(replacements);
- }
-
- public TeXFormatter(FormatterContext formatterContext) {
- super(formatterContext);
- boolean colorful = formatterContext.getProperties().getBoolean(COLORFUL, false);
- out = new ColorfulPrintWriter(formatterContext.getOutputStream(), false, colorful);
- }
-
- @Override
- public void writeStartBatch() {
- super.writeStartBatch();
-
- printCommand("documentclass", "a4paper,twoside", "article", true);
- printCommand("usepackage", "T1", "fontenc", true);
- printCommand("usepackage", "utf8x", "inputenc", true);
- printCommand("usepackage", "pdfauthor={" + Constants.WEBSITE + "}, bookmarks=true,unicode,colorlinks=true,linkcolor=black,urlcolor=blue,citecolor=blue", "hyperref", true);
- printBegin("document");
- }
-
- @Override
- public void writeEndBatch() {
- super.writeEndBatch();
- printEnd("document");
- }
-
- @Override
- public void writeColumnValue(Object value) {
- super.writeColumnValue(value);
- // TODO: arrays, numbers, booleans, nulls etc.:
- out.print(escapeTex(toString(value)));
-
- if (!isCurrentColumnLast()) {
- printColumnSeparator();
- }
- }
-
- @Override
- public void writeEndRow() {
- super.writeEndRow();
- printEndRow();
- }
-
- @Override
- public void writeStartResultSet(ColumnsHeader header) {
- super.writeStartResultSet(header);
- printCommand("begin", null, "tabular", false);
-
- List<ColumnDescriptor> columnDescriptors = header.getColumnDescriptors();
-
- StringBuilder columnAlignments = new StringBuilder();
- for (ColumnDescriptor cd : columnDescriptors) {
- if (cd.isNumeric() || cd.isBoolean()) {
- columnAlignments.append('r');
- } else {
- columnAlignments.append('l');
- }
- }
-
- printCommand(null, null, columnAlignments.toString(), true);
- printCommand("hline", null, null, true);
-
- for (ColumnDescriptor cd : columnDescriptors) {
- printCommand("textbf", null, cd.getLabel(), false);
- if (cd.isLastColumn()) {
- printEndRow();
- } else {
- printColumnSeparator();
- }
- }
-
- printCommand("hline", null, null, true);
- }
-
- @Override
- public void writeEndResultSet() {
- super.writeEndResultSet();
- printCommand("hline", null, null, true);
- printEnd("tabular");
- }
-
- private String escapeTex(String text) {
- if (text == null) {
- return null;
- } else {
- StringBuilder result = new StringBuilder(text.length() * 2);
-
- for (char ch : text.toCharArray()) {
- String replacement = TEX_ESCAPE_MAP.get(ch);
- result.append(replacement == null ? ch : replacement);
- }
-
- return result.toString();
- }
- }
-
- protected String toString(Object value) {
- return String.valueOf(value);
- }
-
- private void printColumnSeparator() {
- out.print(COMMAND_COLOR, " & ");
- }
-
- private void printEndRow() {
- out.println(COMMAND_COLOR, " \\\\");
- out.flush();
- }
-
- /**
- *
- * @param command will not be escaped – should contain just a valid TeX command name
- * @param options will not be escaped – should be properly formatted to be printed inside [
- * and ]
- * @param value will be escaped
- * @param println whether to print line end and flush
- */
- private void printCommand(String command, String options, String value, boolean println) {
-
- if (command != null) {
- out.print(COMMAND_COLOR, "\\" + command);
- }
-
- if (options != null) {
- out.print(COMMAND_COLOR, "[");
- out.print(OPTIONS_COLOR, options);
- out.print(COMMAND_COLOR, "]");
- }
-
- if (value != null) {
- out.print(COMMAND_COLOR, "{");
- out.print(escapeTex(value));
- out.print(COMMAND_COLOR, "}");
- }
-
- if (println) {
- out.println();
- out.flush();
- }
- }
-
- private void printBegin(String environment) {
- printCommand("begin", null, environment, true);
- }
-
- private void printEnd(String environment) {
- printCommand("end", null, environment, true);
- }
-
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/formatting/XhtmlFormatter.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,262 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2014 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.formatting;
-
-import info.globalcode.sql.dk.Constants;
-import info.globalcode.sql.dk.NamedParameter;
-import info.globalcode.sql.dk.Parameter;
-import info.globalcode.sql.dk.Xmlns;
-import info.globalcode.sql.dk.configuration.DatabaseDefinition;
-import info.globalcode.sql.dk.configuration.Properties;
-import info.globalcode.sql.dk.configuration.Property;
-import static info.globalcode.sql.dk.formatting.AbstractXmlFormatter.qname;
-import java.sql.Array;
-import java.sql.SQLException;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-import java.util.Scanner;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import javax.xml.namespace.QName;
-
-/**
- * Prints result sets and parameters as tables, SQL as preformatted and updates counts as
- * paragraphs. You can pick XHTML fragments (usually tabular data) and use it on your website or use
- * whole output as preview or report.
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class XhtmlFormatter extends AbstractXmlFormatter {
-
- private static final Logger log = Logger.getLogger(XhtmlFormatter.class.getName());
- public static final String NAME = "xhtml"; // bash-completion:formatter
- private static final String DOCTYPE = "html PUBLIC \"-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN\" \"http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd\"";
- private static final String CSS_FILE = "info/globalcode/sql/dk/formatter/XhtmlFormatter.css";
- private int statementCounter = 0;
- private int resultSetCounter = 0;
- private int updatesResultCounter = 0;
-
- public XhtmlFormatter(FormatterContext formatterContext) {
- super(addDefaults(formatterContext));
- }
-
- /**
- * Do not indent text – preserve whitespace for pre elements
- */
- private static FormatterContext addDefaults(FormatterContext formatterContext) {
- Properties defaults = new Properties(1);
- defaults.add(new Property(PROPERTY_INDENT_TEXT, "false"));
- formatterContext.getProperties().setLastDefaults(defaults);
- return formatterContext;
- }
-
- @Override
- public void writeStartBatch() {
- super.writeStartBatch();
- printStartDocument();
- printDoctype(DOCTYPE);
- printStartElement(qname("html"), singleAttribute(qname("xmlns"), Xmlns.XHTML));
-
- printStartElement(qname("head"));
- printTextElement(qname("title"), null, Constants.PROGRAM_NAME + ": batch results");
- printCss();
- printEndElement();
-
- printStartElement(qname("body"));
- }
-
- private void printCss() {
-
- try (Scanner css = new Scanner(getClass().getClassLoader().getResourceAsStream(CSS_FILE))) {
- printStartElement(qname("style"), singleAttribute(qname("type"), "text/css"));
- while (css.hasNext()) {
- printText(css.nextLine(), true);
- }
- printEndElement();
- }
- }
-
- @Override
- public void writeEndBatch() {
- super.writeEndBatch();
- printEndElement();
- printEndElement();
- printEndDocument();
- }
-
- @Override
- public void writeStartDatabase(DatabaseDefinition databaseDefinition) {
- super.writeStartDatabase(databaseDefinition);
- printTextElement(qname("h1"), null, "Database: " + databaseDefinition.getName());
-
- printStartElement(qname("p"));
- printText("This is XHTML output of batch executed at: ", true);
- printText(new Date().toString(), true);
- printEndElement();
- }
-
- @Override
- public void writeQuery(String sql) {
- super.writeQuery(sql);
- printTextElement(qname("pre"), null, sql);
- }
-
- @Override
- public void writeParameters(List<? extends Parameter> parameters) {
- super.writeParameters(parameters);
-
- if (parameters == null || parameters.isEmpty()) {
- printTextElement(qname("p"), null, "(this query has no parameters)");
- } else {
- printTextElement(qname("h3"), null, "Parameters:");
-
- printStartElement(qname("table"));
-
- printStartElement(qname("thead"));
- printStartElement(qname("tr"));
- printTextElement(qname("td"), null, "id");
- printTextElement(qname("td"), null, "type");
- printTextElement(qname("td"), null, "value");
- printEndElement();
- printEndElement();
-
- printStartElement(qname("tbody"));
- for (int i = 0; i < parameters.size(); i++) {
- Parameter p = parameters.get(i);
- printStartElement(qname("tr"));
- String numberOrName;
- if (p instanceof NamedParameter) {
- numberOrName = ((NamedParameter) p).getName();
- } else {
- numberOrName = String.valueOf(i + 1);
- }
- printTextElement(qname("td"), null, numberOrName);
- printTextElement(qname("td"), null, p.getType().name());
- printTableData(p.getValue());
- printEndElement();
- }
- printEndElement();
-
- printEndElement();
- }
- }
-
- private void printTableData(Object value) {
-
- if (value instanceof Array) {
- Array sqlArray = (Array) value;
- try {
- Object[] array = (Object[]) sqlArray.getArray();
- printStartElement(qname("td"));
- printArray(array);
- printEndElement();
- } catch (SQLException e) {
- log.log(Level.SEVERE, "Unable to format array", e);
- printTableData(String.valueOf(value));
- }
- } else {
- Map<QName, String> attributes = null;
- if (value instanceof Number) {
- attributes = singleAttribute(qname("class"), "number");
- } else if (value instanceof Boolean) {
- attributes = singleAttribute(qname("class"), "boolean");
- }
- printTextElement(qname("td"), attributes, String.valueOf(value));
- }
- }
-
- private void printArray(Object[] array) {
- printStartElement(qname("ul"));
- for (Object o : array) {
- if (o instanceof Object[]) {
- printStartElement(qname("li"));
- printTextElement(qname("p"), null, "nested array:");
- printArray((Object[]) o);
- printEndElement();
- } else {
- printTextElement(qname("li"), null, String.valueOf(o));
- }
- }
- printEndElement();
- }
-
- @Override
- public void writeStartResultSet(ColumnsHeader header) {
- super.writeStartResultSet(header);
- resultSetCounter++;
- printEmptyElement(qname("hr"), null);
- printTextElement(qname("h3"), null, "Result set #" + resultSetCounter);
- printStartElement(qname("table"));
- printStartElement(qname("thead"));
- printStartElement(qname("tr"));
- for (ColumnDescriptor cd : header.getColumnDescriptors()) {
- // TODO: type
- printTextElement(qname("td"), null, cd.getLabel());
- }
- printEndElement();
- printEndElement();
-
- printStartElement(qname("tbody"));
- }
-
- @Override
- public void writeEndResultSet() {
- super.writeEndResultSet();
- printEndElement();
- printEndElement();
- printTextElement(qname("p"), null, "Record count: " + getCurrentRowCount());
- }
-
- @Override
- public void writeStartRow() {
- super.writeStartRow();
- printStartElement(qname("tr"));
- }
-
- @Override
- public void writeColumnValue(Object value) {
- super.writeColumnValue(value);
- printTableData(value);
- }
-
- @Override
- public void writeEndRow() {
- super.writeEndRow();
- printEndElement();
- }
-
- @Override
- public void writeStartStatement() {
- super.writeStartStatement();
- statementCounter++;
- printEmptyElement(qname("hr"), null);
- printTextElement(qname("h2"), null, "SQL statement #" + statementCounter);
- resultSetCounter = 0;
- updatesResultCounter = 0;
- }
-
- @Override
- public void writeUpdatesResult(int updatedRowsCount) {
- super.writeUpdatesResult(updatedRowsCount);
- updatesResultCounter++;
- printEmptyElement(qname("hr"), null);
- printTextElement(qname("h3"), null, "Updates result #" + updatesResultCounter);
- printTextElement(qname("p"), null, "Updated rows: " + updatedRowsCount);
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/formatting/XmlFormatter.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,245 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.formatting;
-
-import info.globalcode.sql.dk.Parameter;
-import info.globalcode.sql.dk.Xmlns;
-import info.globalcode.sql.dk.configuration.DatabaseDefinition;
-import static info.globalcode.sql.dk.Functions.notNull;
-import info.globalcode.sql.dk.NamedParameter;
-import info.globalcode.sql.dk.configuration.PropertyDeclaration;
-import static info.globalcode.sql.dk.formatting.AbstractXmlFormatter.qname;
-import java.sql.Array;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.SQLXML;
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import javax.xml.namespace.QName;
-
-/**
- * <p>
- * Prints machine-readable output – XML document containing resultsets and updates count. Good
- * choice for further processing – e.g. XSL transformation.</p>
- *
- * <p>
- * TODO: XSD</p>
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-@PropertyDeclaration(name = XmlFormatter.PROPERTY_LABELED_COLUMNS, defaultValue = "false", type = Boolean.class, description = "whether to add 'label' attribute to each 'column' element")
-public class XmlFormatter extends AbstractXmlFormatter {
-
- public static final String NAME = "xml"; // bash-completion:formatter
- public static final String PROPERTY_LABELED_COLUMNS = "labeledColumns";
- private static final Logger log = Logger.getLogger(XmlFormatter.class.getName());
- private final boolean labeledColumns;
-
- public XmlFormatter(FormatterContext formatterContext) {
- super(formatterContext);
- labeledColumns = formatterContext.getProperties().getBoolean(PROPERTY_LABELED_COLUMNS, false);
- }
-
- @Override
- public void writeStartBatch() {
- super.writeStartBatch();
- printStartDocument();
- printStartElement(qname("batchResult"), singleAttribute(qname("xmlns"), Xmlns.BATCH_RESULT));
- }
-
- @Override
- public void writeEndBatch() {
- super.writeEndBatch();
- printEndElement();
- printEndDocument();
- }
-
- @Override
- public void writeStartDatabase(DatabaseDefinition databaseDefinition) {
- super.writeStartDatabase(databaseDefinition);
- Map<QName, String> attributes = databaseDefinition.getName() == null ? null : singleAttribute(qname("name"), databaseDefinition.getName());
- printStartElement(qname("database"), attributes);
- }
-
- @Override
- public void writeEndDatabase() {
- super.writeEndDatabase();
- printEndElement();
- }
-
- @Override
- public void writeStartStatement() {
- super.writeStartStatement();
- printStartElement(qname("statement"));
- }
-
- @Override
- public void writeEndStatement() {
- super.writeEndStatement();
- printEndElement();
- }
-
- @Override
- public void writeQuery(String sql) {
- super.writeQuery(sql);
- printTextElement(qname("sql"), null, sql);
- }
-
- @Override
- public void writeParameters(List<? extends Parameter> parameters) {
- super.writeParameters(parameters);
-
- for (Parameter p : notNull(parameters)) {
-
- Map<QName, String> attributes = new LinkedHashMap<>(2);
- if (p instanceof NamedParameter) {
- attributes.put(qname("name"), ((NamedParameter) p).getName());
- }
- attributes.put(qname("type"), p.getType().name());
-
- printTextElement(qname("parameter"), attributes, String.valueOf(p.getValue()));
- }
-
- }
-
- @Override
- public void writeStartResultSet(ColumnsHeader header) {
- super.writeStartResultSet(header);
- printStartElement(qname("resultSet"));
-
- for (ColumnDescriptor cd : header.getColumnDescriptors()) {
- Map<QName, String> attributes = new LinkedHashMap<>(4);
- attributes.put(qname("label"), cd.getLabel());
- attributes.put(qname("name"), cd.getName());
- attributes.put(qname("typeName"), cd.getTypeName());
- attributes.put(qname("type"), String.valueOf(cd.getType()));
- printEmptyElement(qname("columnHeader"), attributes);
- }
- }
-
- @Override
- public void writeEndResultSet() {
- super.writeEndResultSet();
- printEndElement();
- }
-
- @Override
- public void writeStartRow() {
- super.writeStartRow();
- printStartElement(qname("row"));
- }
-
- @Override
- public void writeColumnValue(Object value) {
- super.writeColumnValue(value);
-
- Map<QName, String> attributes = null;
- if (labeledColumns) {
- attributes = new LinkedHashMap<>(2);
- attributes.put(qname("label"), getCurrentColumnsHeader().getColumnDescriptors().get(getCurrentColumnsCount() - 1).getLabel());
- }
-
- if (value == null) {
- if (attributes == null) {
- attributes = new LinkedHashMap<>(2);
- }
- attributes.put(qname("null"), "true");
- printEmptyElement(qname("column"), attributes);
- } else if (value instanceof Array) {
-
- Array sqlArray = (Array) value;
- try {
- Object[] array = (Object[]) sqlArray.getArray();
- printStartElement(qname("column"), attributes);
- printArray(array);
- printEndElement();
- } catch (SQLException e) {
- // FIXME: rewrite array formatting, remember array mode, don't try sqlArray.getArray() again and again if it has failed
- log.log(Level.SEVERE, "Unable to format array", e);
- try {
- ResultSet arrayResultSet = sqlArray.getResultSet();
- //int columnCount = arrayResultSet.getMetaData().getColumnCount();
- ArrayList<Object> arrayList = new ArrayList<>();
- while (arrayResultSet.next()) {
- arrayList.add(arrayResultSet.getObject(2));
- // for (int i = 1; i <= columnCount; i++) {
- // log.log(Level.INFO, "Array column {0} = {1}", new Object[]{i, arrayResultSet.getObject(i)});
- // }
- }
-
- printStartElement(qname("column"), attributes);
- // FIXME: instanceof SQLXML, see below
- printArray(arrayList.toArray());
- printEndElement();
-
- } catch (SQLException e2) {
- // FIXME: fix logging, error recovery
- log.log(Level.SEVERE, "Second level fuck up !!!", e2);
- }
-
- writeColumnValue(String.valueOf(value));
- }
-
- } else if (value instanceof SQLXML) { // FIXME: move to separate method, to AbstractFormatter?
- SQLXML xml = (SQLXML) value;
- // TODO: parse DOM/SAX and transplant XML, don't escape (optional)
- try {
- printTextElement(qname("column"), attributes, xml.getString());
- } catch (SQLException e) {
- log.log(Level.SEVERE, "Unable to format XML", e);
- writeColumnValue(String.valueOf(value));
- }
- } else {
- printTextElement(qname("column"), attributes, toString(value));
- }
- }
-
- private void printArray(Object[] array) {
- printStartElement(qname("array"));
- for (Object o : array) {
- if (o instanceof Object[]) {
- printStartElement(qname("item"));
- printArray((Object[]) o);
- printEndElement();
- } else {
- printTextElement(qname("item"), null, String.valueOf(o));
- }
- }
- printEndElement();
- }
-
- @Override
- public void writeEndRow() {
- super.writeEndRow();
- printEndElement();
- }
-
- @Override
- public void writeUpdatesResult(int updatedRowsCount) {
- super.writeUpdatesResult(updatedRowsCount);
- printTextElement(qname("updatedRows"), null, String.valueOf(updatedRowsCount));
- }
-
- protected String toString(Object value) {
- return String.valueOf(value);
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/jmx/ConnectionManagement.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,97 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2014 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.jmx;
-
-import java.util.EnumMap;
-import java.util.Map;
-
-/**
- * JMX management bean for progress reporting.
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class ConnectionManagement implements ConnectionManagementMBean {
-
- private final String databaseName;
- private final Map<COUNTER, Integer> counters = new EnumMap(COUNTER.class);
-
- public ConnectionManagement(String databaseName) {
- this.databaseName = databaseName;
- for (COUNTER c : COUNTER.values()) {
- counters.put(c, 0);
- }
- }
-
- public enum COUNTER {
-
- COMMAND,
- RECORD_CURRENT,
- RECORD_TOTAL
- };
-
- public void incrementCounter(COUNTER counter) {
- synchronized (counters) {
- int old = counters.get(counter);
- counters.put(counter, old + 1);
- }
- }
-
- public void resetCounter(COUNTER counter) {
- synchronized (counters) {
- counters.put(counter, 0);
- }
- }
-
- public static void incrementCounter(ConnectionManagement mbean, COUNTER counter) {
- if (mbean != null) {
- mbean.incrementCounter(counter);
- }
- }
-
- public static void resetCounter(ConnectionManagement mbean, COUNTER counter) {
- if (mbean != null) {
- mbean.resetCounter(counter);
- }
- }
-
- @Override
- public String getDatabaseName() {
- return databaseName;
- }
-
- @Override
- public int getCommandCount() {
- synchronized (counters) {
- return counters.get(COUNTER.COMMAND);
- }
- }
-
- @Override
- public int getCurrentRecordCount() {
- synchronized (counters) {
- return counters.get(COUNTER.RECORD_CURRENT);
- }
- }
-
- @Override
- public int getTotalRecordCount() {
- synchronized (counters) {
- return counters.get(COUNTER.RECORD_TOTAL);
- }
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/jmx/ConnectionManagementMBean.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2014 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.jmx;
-
-/**
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public interface ConnectionManagementMBean {
-
- public String getDatabaseName();
-
- public int getCommandCount();
-
- public int getCurrentRecordCount();
-
- public int getTotalRecordCount();
-
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/jmx/ManagementUtils.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2014 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.jmx;
-
-import java.lang.management.ManagementFactory;
-import java.util.Hashtable;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import javax.management.MBeanServer;
-import javax.management.ObjectName;
-
-/**
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class ManagementUtils {
-
- private static final Logger log = Logger.getLogger(ManagementUtils.class.getName());
- public static final String DEFAULT_CONNECTION_JMX_NAME = "main";
-
- /**
- * @see #registerMBean(java.lang.String, java.lang.String) with default JMX name
- */
- public static ConnectionManagement registerMBean(String dbName) {
- return registerMBean(dbName, DEFAULT_CONNECTION_JMX_NAME);
- }
-
- /**
- *
- * @param dbName database name
- * @param jmxName name of JMX bean
- * @return registered JMX bean | or null if registration fails (should not)
- */
- public static ConnectionManagement registerMBean(String dbName, String jmxName) {
- try {
- ConnectionManagement mbean = new ConnectionManagement(dbName);
- MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
- Hashtable<String, String> objectProperties = new Hashtable<>();
- objectProperties.put("type", "Connection");
- objectProperties.put("name", jmxName);
- ObjectName objectName = new ObjectName("info.globalcode.sql.dk", objectProperties);
- mbs.registerMBean(mbean, objectName);
- log.log(Level.FINE, "JMX MBean was registered as: {0}", objectName);
- return mbean;
- } catch (Exception e) {
- log.log(Level.WARNING, "Unable to register JMX MBean", e);
- return null;
- }
- }
-
- private ManagementUtils() {
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/logging/ColorfulConsoleFormatter.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,97 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.logging;
-
-import info.globalcode.sql.dk.ColorfulPrintWriter;
-import static info.globalcode.sql.dk.ColorfulPrintWriter.TerminalColor;
-import static info.globalcode.sql.dk.ColorfulPrintWriter.TerminalStyle;
-import static info.globalcode.sql.dk.Functions.rpad;
-import java.io.StringWriter;
-import java.util.logging.Formatter;
-import java.util.logging.Level;
-import java.util.logging.LogRecord;
-
-/**
- * For console/terminal log output. Log messages are printed in brief and colorful form.
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class ColorfulConsoleFormatter extends Formatter {
-
- private boolean printStacktrace = false;
-
- @Override
- public String format(LogRecord r) {
- StringWriter sw = new StringWriter();
- try (ColorfulPrintWriter out = new ColorfulPrintWriter(sw)) {
- printLevel(out, r.getLevel());
- printMessage(out, r);
- printThrowable(out, r);
- out.println();
- }
- return sw.toString();
- }
-
- private void printLevel(ColorfulPrintWriter out, Level l) {
- TerminalColor color = TerminalColor.Magenta;
-
- if (l == Level.SEVERE) {
- color = TerminalColor.Red;
- } else if (l == Level.WARNING) {
- color = TerminalColor.Yellow;
- }
-
- out.print(color, rpad(l.getLocalizedName() + ": ", 10));
- }
-
- private void printMessage(ColorfulPrintWriter out, LogRecord r) {
- out.print(formatMessage(r));
- }
-
- private void printThrowable(ColorfulPrintWriter out, LogRecord r) {
- Throwable t = r.getThrown();
- if (t != null) {
- out.print(": ");
- out.print(TerminalColor.Red, t.getClass().getSimpleName());
- String message = t.getLocalizedMessage();
- if (message != null) {
- out.print(": ");
- if (printStacktrace) {
- out.print(message);
- } else {
- out.print(message.replaceAll("\\n", " "));
- }
- }
- if (printStacktrace) {
- out.println();
- out.setForegroundColor(TerminalColor.Yellow);
- out.setStyle(TerminalStyle.Dim);
- t.printStackTrace(out);
- out.resetAll();
- }
- }
- }
-
- public boolean isPrintStacktrace() {
- return printStacktrace;
- }
-
- public void setPrintStacktrace(boolean printStacktrace) {
- this.printStacktrace = printStacktrace;
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/logging/LoggerInitializer.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,78 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.logging;
-
-import info.globalcode.sql.dk.Constants;
-import java.util.logging.ConsoleHandler;
-import java.util.logging.Handler;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * Configures logging subsystem.
- * Usage: java -Djava.util.logging.config.class=info.globalcode.sql.dk.logging.LoggerInitializer …
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class LoggerInitializer {
-
- private static final Logger log = Logger.getLogger(LoggerInitializer.class.getName());
- public static final String LEVEL_PROPERTY = LoggerInitializer.class.getName() + ".level";
- private static final Level DEFAULT_LEVEL = Level.INFO;
-
- public LoggerInitializer() {
- Logger logger = Logger.getLogger(Constants.JAVA_PACKAGE);
- ConsoleHandler handler = new ConsoleHandler();
- ColorfulConsoleFormatter formatter = new ColorfulConsoleFormatter();
-
- logger.addHandler(handler);
- handler.setFormatter(formatter);
-
- setLevel(logger, handler, formatter);
-
-
- /**
- * TODO: optional FileHandler – detailed logs in file in ~/sql-dk/log/…
- */
- }
-
- private void setLevel(Logger logger, Handler handler, ColorfulConsoleFormatter formatter) {
- boolean levelParseError = false;
- Level level;
- String cliLevel = System.getProperty(LEVEL_PROPERTY);
- if (cliLevel == null) {
- level = DEFAULT_LEVEL;
- } else {
- try {
- level = Level.parse(cliLevel);
- } catch (IllegalArgumentException e) {
- level = DEFAULT_LEVEL;
- levelParseError = true;
- }
- }
-
- handler.setLevel(level);
- logger.setLevel(level);
-
- if (levelParseError) {
- log.log(Level.WARNING, "Invalid logging level „{0}“ specified in „{1}“ → using default level „{2}“", new Object[]{cliLevel, LEVEL_PROPERTY, DEFAULT_LEVEL});
- }
-
- formatter.setPrintStacktrace(level.intValue() < Level.INFO.intValue());
- }
-}
--- a/java/sql-dk/src/info/globalcode/sql/dk/logging/LoggerProducer.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2015 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk.logging;
-
-import java.util.logging.Logger;
-
-/**
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class LoggerProducer {
-
- /**
- * @return created logger for the caller class
- */
- public static Logger getLogger() {
- String className = Thread.currentThread().getStackTrace()[2].getClassName();
- return Logger.getLogger(className);
- }
-
-}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/CLIOptions.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,283 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk;
+
+import static info.globalcode.sql.dk.Functions.isNotEmpty;
+import static info.globalcode.sql.dk.Functions.equalz;
+import info.globalcode.sql.dk.InfoLister.InfoType;
+import info.globalcode.sql.dk.configuration.Properties;
+import info.globalcode.sql.dk.configuration.Property;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+/**
+ * Holds options from command line, validates them, combines with configuration and provides derived
+ * objects.
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class CLIOptions {
+
+ public static final String DEFAULT_NAME_PREFIX = ":";
+ public static final String DEFAULT_NAME_SUFFIX = "(?=([^\\w]|$))";
+ private String sql;
+ private String databaseName;
+ private final Set<String> databaseNamesToTest = new LinkedHashSet<>();
+ private final Set<String> databaseNamesToListProperties = new LinkedHashSet<>();
+ private final Set<String> formatterNamesToListProperties = new LinkedHashSet<>();
+ private String namePrefix = DEFAULT_NAME_PREFIX;
+ private String nameSuffix = DEFAULT_NAME_SUFFIX;
+ private String formatterName;
+ private boolean batch;
+ private final Properties formatterProperties = new Properties();
+ private final Properties databaseProperties = new Properties();
+
+ public enum MODE {
+
+ QUERY_NOW,
+ PREPARE_BATCH,
+ EXECUTE_BATCH,
+ JUST_SHOW_INFO
+ }
+ private final List<NamedParameter> namedParameters = new ArrayList<>();
+ private final List<Parameter> numberedParameters = new ArrayList<>();
+ private final EnumSet<InfoType> showInfo = EnumSet.noneOf(InfoType.class);
+
+ public void validate() throws InvalidOptionsException {
+ InvalidOptionsException e = new InvalidOptionsException();
+
+ MODE mode = getMode();
+ if (mode == null) {
+ e.addProblem(new InvalidOptionsException.OptionProblem("Invalid combination of DB, SQL and BATCH – please specify just 2 of this 3 options"));
+ } else if (mode == MODE.JUST_SHOW_INFO) {
+ if (!namedParameters.isEmpty()) {
+ e.addProblem(new InvalidOptionsException.OptionProblem("Do not use named parameters if just showing info."));
+ }
+ if (!numberedParameters.isEmpty()) {
+ e.addProblem(new InvalidOptionsException.OptionProblem("Do not use numbered parameters if just showing info."));
+ }
+ if (isNotEmpty(sql, false)) {
+ e.addProblem(new InvalidOptionsException.OptionProblem("Do not specify SQL if just showing info."));
+ }
+ if (isNotEmpty(databaseName, false)) {
+ e.addProblem(new InvalidOptionsException.OptionProblem("Do not specify database if just showing info."));
+ }
+ if (batch) {
+ e.addProblem(new InvalidOptionsException.OptionProblem("Do not specify batch if just showing info."));
+ }
+ if (!equalz(namePrefix, DEFAULT_NAME_PREFIX)) {
+ e.addProblem(new InvalidOptionsException.OptionProblem("Do not specify name prefix if just showing info."));
+ }
+ if (!equalz(nameSuffix, DEFAULT_NAME_SUFFIX)) {
+ e.addProblem(new InvalidOptionsException.OptionProblem("Do not specify name suffix if just showing info."));
+ }
+ if (showInfo.contains(InfoType.CONNECTION) && databaseNamesToTest.isEmpty()) {
+ e.addProblem(new InvalidOptionsException.OptionProblem("Please specify which database should be tested."));
+ }
+ if (showInfo.contains(InfoType.JDBC_PROPERTIES) && databaseNamesToListProperties.isEmpty()) {
+ e.addProblem(new InvalidOptionsException.OptionProblem("Please specify for which database the properties should be listed."));
+ }
+ }
+
+ if (!namedParameters.isEmpty() && !numberedParameters.isEmpty()) {
+ e.addProblem(new InvalidOptionsException.OptionProblem("Named and numbered parameters can not be used together in one command."));
+ }
+
+ try {
+ Pattern.compile(namePrefix + "test" + nameSuffix);
+ } catch (PatternSyntaxException regexException) {
+ e.addProblem(new InvalidOptionsException.OptionProblem("Ivalid regular expression in name prefix or suffix", regexException));
+ }
+
+ if (e.hasProblems()) {
+ throw e;
+ }
+ }
+
+ private boolean hasSql() {
+ return isNotEmpty(getSql(), true);
+ }
+
+ private boolean hasDb() {
+ return isNotEmpty(getDatabaseName(), true);
+ }
+
+ /**
+ * Depends on options: DB, BATCH, SQL
+ *
+ * @return mode | or null if options are not yet initialized or combination of options is
+ * invalid
+ */
+ public MODE getMode() {
+ if (hasDb() && !batch && hasSql()) {
+ return MODE.QUERY_NOW;
+ } else if (!hasDb() && batch && hasSql()) {
+ return MODE.PREPARE_BATCH;
+ } else if (hasDb() && batch && !hasSql()) {
+ return MODE.EXECUTE_BATCH;
+ } else {
+ return showInfo.isEmpty() ? null : MODE.JUST_SHOW_INFO;
+ }
+ }
+
+ public String getSql() {
+ return sql;
+ }
+
+ public void setSql(String sql) {
+ this.sql = sql;
+ }
+
+ public String getDatabaseName() {
+ return databaseName;
+ }
+
+ public void setDatabaseName(String databaseName) {
+ this.databaseName = databaseName;
+ }
+
+ public void setBatch(boolean batch) {
+ this.batch = batch;
+ }
+
+ public Collection<NamedParameter> getNamedParameters() {
+ return namedParameters;
+ }
+
+ public List<Parameter> getNumberedParameters() {
+ return numberedParameters;
+ }
+
+ public void addNumberedParameter(Parameter p) {
+ numberedParameters.add(p);
+ }
+
+ public void addNamedParameter(NamedParameter p) {
+ namedParameters.add(p);
+ }
+
+ public Properties getDatabaseProperties() {
+ return databaseProperties;
+ }
+
+ public Properties getFormatterProperties() {
+ return formatterProperties;
+ }
+
+ public void addDatabaseProperty(Property p) {
+ databaseProperties.add(p);
+ }
+
+ public void addFormatterProperty(Property p) {
+ formatterProperties.add(p);
+ }
+
+ /**
+ * @return regular expression describing the name prefix
+ */
+ public String getNamePrefix() {
+ return namePrefix;
+ }
+
+ /**
+ * @param namePrefix
+ * @see #getNamePrefix()
+ */
+ public void setNamePrefix(String namePrefix) {
+ this.namePrefix = namePrefix;
+ }
+
+ /**
+ * @return regular expression describing the name prefix
+ */
+ public String getNameSuffix() {
+ return nameSuffix;
+ }
+
+ /**
+ * @param nameSuffix
+ * @see #getNameSuffix()
+ */
+ public void setNameSuffix(String nameSuffix) {
+ this.nameSuffix = nameSuffix;
+ }
+
+ public String getFormatterName() {
+ return formatterName;
+ }
+
+ public void setFormatterName(String formatterName) {
+ this.formatterName = formatterName;
+ }
+
+ public void addShowInfo(InfoType info) {
+ showInfo.add(info);
+ }
+
+ public EnumSet<InfoType> getShowInfo() {
+ return showInfo;
+ }
+
+ public Set<String> getDatabaseNamesToTest() {
+ return databaseNamesToTest;
+ }
+
+ public void addDatabaseNameToTest(String name) {
+ databaseNamesToTest.add(name);
+ }
+
+ public Set<String> getDatabaseNamesToListProperties() {
+ return databaseNamesToListProperties;
+ }
+
+ public void addDatabaseNameToListProperties(String name) {
+ databaseNamesToListProperties.add(name);
+ }
+
+ public Set<String> getFormatterNamesToListProperties() {
+ return formatterNamesToListProperties;
+ }
+
+ public void addFormatterNameToListProperties(String name) {
+ formatterNamesToListProperties.add(name);
+ }
+
+ public SQLCommand getSQLCommand() {
+ if (namedParameters.isEmpty()) {
+ return new SQLCommandNumbered(sql, numberedParameters);
+ } else {
+ return new SQLCommandNamed(sql, namedParameters, namePrefix, nameSuffix);
+ }
+ }
+
+ public OutputStream getOutputStream() {
+ return System.out;
+ }
+
+ public InputStream getInputStream() {
+ return System.in;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/CLIParser.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,216 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk;
+
+import static info.globalcode.sql.dk.Functions.readString;
+import info.globalcode.sql.dk.InfoLister.InfoType;
+import info.globalcode.sql.dk.configuration.Property;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Converts command line arguments from String array to object.
+ * Checks basic constraints (if only supported options are used and if they have correct number of
+ * parameters)
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class CLIParser {
+
+ public static final String TYPE_NAME_SEPARATOR = ":";
+
+ public CLIOptions parseOptions(String[] args, InputStream in) throws CLIParserException {
+ CLIOptions options = new CLIOptions();
+
+ List<SQLType> numberedTypes = new ArrayList<>();
+ Map<String, SQLType> namedTypes = new HashMap<>();
+
+ for (int i = 0; i < args.length; i++) {
+ String arg = args[i];
+ switch (arg) {
+ case Tokens.TYPES:
+ String typesString = fetchNext(args, ++i);
+
+ for (String oneType : typesString.split(",")) {
+ int sepatratorIndex = oneType.indexOf(TYPE_NAME_SEPARATOR);
+ if (sepatratorIndex == -1) {
+ numberedTypes.add(getType(oneType.toUpperCase()));
+ } else {
+ String namePart = oneType.substring(0, sepatratorIndex).trim();
+ String typePart = oneType.substring(sepatratorIndex + TYPE_NAME_SEPARATOR.length(), oneType.length());
+ namedTypes.put(namePart, getType(typePart.toUpperCase()));
+ }
+ }
+ break;
+ case Tokens.NAME_PREFIX:
+ options.setNamePrefix(fetchNext(args, ++i));
+ break;
+ case Tokens.NAME_SUFFIX:
+ options.setNameSuffix(fetchNext(args, ++i));
+ break;
+ case Tokens.DB:
+ options.setDatabaseName(fetchNext(args, ++i));
+ break;
+ case Tokens.SQL:
+ options.setSql(fetchNext(args, ++i));
+ break;
+ case Tokens.SQL_IN:
+ try {
+ options.setSql(readString(in));
+ } catch (IOException e) {
+ throw new CLIParserException("Unable to read SQL from the input stream", e);
+ }
+ break;
+ case Tokens.BATCH:
+ options.setBatch(true);
+ break;
+ case Tokens.DATA: // --data is the last option
+ for (i++; i < args.length; i++) {
+ arg = args[i];
+ Parameter parameter;
+ if (numberedTypes.isEmpty()) {
+ parameter = new Parameter(arg, null);
+ } else {
+ int paramIndex = options.getNumberedParameters().size();
+ SQLType paramType;
+ try {
+ paramType = numberedTypes.get(paramIndex);
+ } catch (IndexOutOfBoundsException e) {
+ throw new CLIParserException("Missing type for parameter #" + paramIndex, e);
+ } catch (NullPointerException e) {
+ throw new CLIParserException("Invalid type definition for parameter #" + paramIndex, e);
+ }
+ parameter = new Parameter(arg, paramType);
+ }
+ options.addNumberedParameter(parameter);
+ }
+ break;
+ case Tokens.DATA_NAMED:
+ for (i++; i < args.length; i++) {
+ String paramName = args[i];
+ String paramValue = fetchNext(args, ++i);
+ options.addNamedParameter(new NamedParameter(paramName, paramValue, namedTypes.get(paramName)));
+ }
+ break;
+ case Tokens.FORMATTER:
+ options.setFormatterName(fetchNext(args, ++i));
+ break;
+ case Tokens.DB_PROPERTY:
+ options.addDatabaseProperty(new Property(fetchNext(args, ++i), fetchNext(args, ++i)));
+ break;
+ case Tokens.FORMATTER_PROPERTY:
+ options.addFormatterProperty(new Property(fetchNext(args, ++i), fetchNext(args, ++i)));
+ break;
+ case Tokens.INFO_HELP:
+ options.addShowInfo(InfoType.HELP);
+ break;
+ case Tokens.INFO_FORMATTERS:
+ options.addShowInfo(InfoType.FORMATTERS);
+ break;
+ case Tokens.INFO_FORMATTER_PROPERTIES:
+ options.addShowInfo(InfoType.FORMATTER_PROPERTIES);
+ options.addFormatterNameToListProperties(fetchNext(args, ++i));
+ break;
+ case Tokens.INFO_LICENSE:
+ options.addShowInfo(InfoType.LICENSE);
+ break;
+ case Tokens.INFO_JAVA_PROPERTIES:
+ options.addShowInfo(InfoType.JAVA_PROPERTIES);
+ break;
+ case Tokens.INFO_ENVIRONMENT_VARIABLES:
+ options.addShowInfo(InfoType.ENVIRONMENT_VARIABLES);
+ break;
+ case Tokens.INFO_TYPES:
+ options.addShowInfo(InfoType.TYPES);
+ break;
+ case Tokens.INFO_VERSION:
+ options.addShowInfo(InfoType.VERSION);
+ break;
+ case Tokens.INFO_JDBC_DRIVERS:
+ options.addShowInfo(InfoType.JDBC_DRIVERS);
+ break;
+ case Tokens.INFO_JDBC_PROPERTIES:
+ options.addShowInfo(InfoType.JDBC_PROPERTIES);
+ options.addDatabaseNameToListProperties(fetchNext(args, ++i));
+ break;
+ case Tokens.INFO_DATABASES:
+ options.addShowInfo(InfoType.DATABASES);
+ break;
+ case Tokens.INFO_CONNECTION:
+ options.addShowInfo(InfoType.CONNECTION);
+ options.addDatabaseNameToTest(fetchNext(args, ++i));
+ break;
+ default:
+ throw new CLIParserException("Unknown option: " + arg);
+ }
+ }
+ return options;
+ }
+
+ private String fetchNext(String[] args, int index) throws CLIParserException {
+ if (index < args.length) {
+ return args[index];
+ } else {
+ throw new CLIParserException("Expecting value for option: " + args[index - 1]);
+ }
+ }
+
+ public static class Tokens {
+
+ // bash-completion:options:
+ public static final String DB = "--db"; // bash-completion:option // help: database name
+ public static final String DB_PROPERTY = "--db-property"; // bash-completion:option // help: name and value
+ public static final String SQL = "--sql"; // bash-completion:option // help: SQL query/command
+ public static final String SQL_IN = "--sql-in"; // bash-completion:option // help: SQL query/command
+ public static final String BATCH = "--batch"; // bash-completion:option // help: batch mode (no argument)
+ public static final String DATA = "--data"; // bash-completion:option // help: list of ordinal parameters
+ public static final String DATA_NAMED = "--data-named"; // bash-completion:option // help: list of named parameters
+ public static final String NAME_PREFIX = "--name-prefix"; // bash-completion:option // help: parameter name prefix – regular expression
+ public static final String NAME_SUFFIX = "--name-suffix"; // bash-completion:option // help: parameter name suffix – regular expression
+ public static final String TYPES = "--types"; // bash-completion:option // help: comma separated list of parameter types
+ public static final String FORMATTER = "--formatter"; // bash-completion:option // help: name of the output formatter
+ public static final String FORMATTER_PROPERTY = "--formatter-property"; // bash-completion:option // help: name and value
+ public static final String INFO_HELP = "--help"; // bash-completion:option // help: print this help
+ public static final String INFO_VERSION = "--version"; // bash-completion:option // help: print version info
+ public static final String INFO_LICENSE = "--license"; // bash-completion:option // help: print license
+ public static final String INFO_JAVA_PROPERTIES = "--list-java-properties"; // bash-completion:option // help: list of Java system properties
+ public static final String INFO_ENVIRONMENT_VARIABLES = "--list-environment-variables"; // bash-completion:option // help: list of environment variables
+ public static final String INFO_FORMATTERS = "--list-formatters"; // bash-completion:option // help: print list of available formatters
+ public static final String INFO_FORMATTER_PROPERTIES = "--list-formatter-properties"; // bash-completion:option // help: print list of available properties for given formatter
+ public static final String INFO_TYPES = "--list-types"; // bash-completion:option // help: print list of available data types
+ public static final String INFO_JDBC_DRIVERS = "--list-jdbc-drivers"; // bash-completion:option // help: list of available JDBC drivers
+ public static final String INFO_JDBC_PROPERTIES = "--list-jdbc-properties"; // bash-completion:option // help: list of available JDBC properties for given database
+ public static final String INFO_DATABASES = "--list-databases"; // bash-completion:option // help: print list of configured databases
+ public static final String INFO_CONNECTION = "--test-connection"; // bash-completion:option // help: test connection to particular database
+
+ private Tokens() {
+ }
+ }
+
+ private SQLType getType(String typeString) throws CLIParserException {
+ try {
+ return SQLType.valueOf(typeString.trim());
+ } catch (IllegalArgumentException e) {
+ throw new CLIParserException("Unsupported type: " + typeString, e);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/CLIParserException.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,40 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk;
+
+/**
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class CLIParserException extends DKException {
+
+ public CLIParserException() {
+ }
+
+ public CLIParserException(String message) {
+ super(message);
+ }
+
+ public CLIParserException(Throwable cause) {
+ super(cause);
+ }
+
+ public CLIParserException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/CLIStarter.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,274 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk;
+
+import info.globalcode.sql.dk.configuration.ConfigurationProvider;
+import info.globalcode.sql.dk.CLIOptions.MODE;
+import info.globalcode.sql.dk.batch.Batch;
+import info.globalcode.sql.dk.batch.BatchDecoder;
+import info.globalcode.sql.dk.batch.BatchException;
+import info.globalcode.sql.dk.batch.BatchEncoder;
+import info.globalcode.sql.dk.configuration.Configuration;
+import info.globalcode.sql.dk.configuration.ConfigurationException;
+import info.globalcode.sql.dk.configuration.DatabaseDefinition;
+import info.globalcode.sql.dk.configuration.FormatterDefinition;
+import info.globalcode.sql.dk.configuration.Loader;
+import info.globalcode.sql.dk.configuration.NameIdentified;
+import info.globalcode.sql.dk.configuration.PropertyDeclaration;
+import info.globalcode.sql.dk.formatting.Formatter;
+import info.globalcode.sql.dk.formatting.FormatterContext;
+import info.globalcode.sql.dk.formatting.FormatterException;
+import info.globalcode.sql.dk.jmx.ConnectionManagement;
+import info.globalcode.sql.dk.jmx.ManagementUtils;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.sql.SQLException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+
+/**
+ * Entry point of the command line interface of SQL-DK.
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class CLIStarter implements ConfigurationProvider {
+
+ // help:exit-codes
+ public static final int EXIT_SUCCESS = 0; // doc:success
+ public static final int EXIT_UNEXPECTED_ERROR = 1; // doc:unexpected error (probably bug)
+ // 2 is reserved: http://www.tldp.org/LDP/abs/html/exitcodes.html#EXITCODESREF
+ public static final int EXIT_SQL_ERROR = 3; // doc:SQL error
+ public static final int EXIT_CLI_PARSE_ERROR = 4; // doc:CLI options parse error
+ public static final int EXIT_CLI_VALIDATE_ERROR = 5; // doc:CLI options validation error
+ public static final int EXIT_CONFIGURATION_ERROR = 6; // doc:configuration error
+ public static final int EXIT_FORMATTING_ERROR = 7; // doc:formatting error
+ public static final int EXIT_BATCH_ERROR = 8; // doc:batch error
+ private static final Logger log = Logger.getLogger(CLIStarter.class.getName());
+ private final CLIOptions options;
+ private final Loader configurationLoader = new Loader();
+ private Configuration configuration;
+
+ public static void main(String[] args) {
+ log.log(Level.FINE, "Starting " + Constants.PROGRAM_NAME);
+ int exitCode;
+
+ if (args.length == 0) {
+ args = new String[]{CLIParser.Tokens.INFO_HELP};
+ }
+
+ try {
+ CLIParser parser = new CLIParser();
+ CLIOptions options = parser.parseOptions(args, System.in);
+ options.validate();
+ CLIStarter starter = new CLIStarter(options);
+ starter.installDefaultConfiguration();
+ starter.process();
+ log.log(Level.FINE, "All done");
+ exitCode = EXIT_SUCCESS;
+ } catch (CLIParserException e) {
+ log.log(Level.SEVERE, "Unable to parse CLI options", e);
+ exitCode = EXIT_CLI_PARSE_ERROR;
+ } catch (InvalidOptionsException e) {
+ log.log(Level.SEVERE, "Invalid CLI options", e);
+ for (InvalidOptionsException.OptionProblem p : e.getProblems()) {
+ LogRecord r = new LogRecord(Level.SEVERE, "Option problem: {0}");
+ r.setThrown(p.getException());
+ r.setParameters(new Object[]{p.getDescription()});
+ log.log(r);
+ }
+ exitCode = EXIT_CLI_VALIDATE_ERROR;
+ } catch (ConfigurationException e) {
+ log.log(Level.SEVERE, "Configuration problem", e);
+ exitCode = EXIT_CONFIGURATION_ERROR;
+ } catch (SQLException e) {
+ log.log(Level.SEVERE, "SQL problem", e);
+ exitCode = EXIT_SQL_ERROR;
+ } catch (FormatterException e) {
+ log.log(Level.SEVERE, "Formatting problem", e);
+ exitCode = EXIT_FORMATTING_ERROR;
+ } catch (BatchException e) {
+ log.log(Level.SEVERE, "Batch problem", e);
+ exitCode = EXIT_BATCH_ERROR;
+ }
+
+ System.exit(exitCode);
+ }
+
+ public CLIStarter(CLIOptions options) {
+ this.options = options;
+ }
+
+ private void process() throws ConfigurationException, SQLException, FormatterException, BatchException {
+ MODE mode = options.getMode();
+
+ /** Show info */
+ if (!options.getShowInfo().isEmpty()) {
+ PrintStream infoOut = mode == MODE.JUST_SHOW_INFO ? System.out : System.err;
+ InfoLister infoLister = new InfoLister(infoOut, this, options);
+ infoLister.showInfo();
+ }
+
+ switch (mode) {
+ case QUERY_NOW:
+ processQueryNow();
+ break;
+ case PREPARE_BATCH:
+ processPrepareBatch();
+ break;
+ case EXECUTE_BATCH:
+ processExecuteBatch();
+ break;
+ case JUST_SHOW_INFO:
+ // already done above
+ break;
+ default:
+ log.log(Level.SEVERE, "Unsupported mode: {0}", mode);
+ break;
+ }
+
+ generateBashCompletion();
+ }
+
+ private void processQueryNow() throws ConfigurationException, SQLException, FormatterException {
+ DatabaseDefinition dd = getConfiguration().getDatabase(options.getDatabaseName());
+ FormatterDefinition fd = configuration.getFormatter(options.getFormatterName());
+ ConnectionManagement jmxBean = ManagementUtils.registerMBean(dd.getName());
+
+ try (DatabaseConnection c = dd.connect(options.getDatabaseProperties(), jmxBean)) {
+ log.log(Level.FINE, "Database connected");
+ try (Formatter f = fd.getInstance(new FormatterContext(options.getOutputStream(), options.getFormatterProperties()))) {
+ c.executeQuery(options.getSQLCommand(), f);
+ }
+ }
+ }
+
+ private void processPrepareBatch() throws BatchException {
+ BatchEncoder enc = new BatchEncoder();
+ int length = enc.encode(options.getSQLCommand(), options.getOutputStream());
+ log.log(Level.FINE, "Prepared batch size: {0} bytes", length);
+ }
+
+ private void processExecuteBatch() throws ConfigurationException, SQLException, FormatterException, BatchException {
+ BatchDecoder dec = new BatchDecoder();
+ Batch b = dec.decode(options.getInputStream());
+
+ DatabaseDefinition dd = getConfiguration().getDatabase(options.getDatabaseName());
+ FormatterDefinition fd = configuration.getFormatter(options.getFormatterName());
+ ConnectionManagement jmxBean = ManagementUtils.registerMBean(dd.getName());
+
+ try (DatabaseConnection c = dd.connect(options.getDatabaseProperties(), jmxBean)) {
+ log.log(Level.FINE, "Database connected");
+ try (Formatter f = fd.getInstance(new FormatterContext(options.getOutputStream(), options.getFormatterProperties()))) {
+ c.executeBatch(b, f);
+ }
+ }
+ }
+
+ @Override
+ public Configuration getConfiguration() throws ConfigurationException {
+ if (configuration == null) {
+ configuration = configurationLoader.loadConfiguration();
+ }
+ return configuration;
+ }
+
+ private void installDefaultConfiguration() throws ConfigurationException {
+ Constants.DIR.mkdir();
+
+ if (Constants.CONFIG_FILE.exists()) {
+ log.log(Level.FINER, "Config file already exists: {0}", Constants.CONFIG_FILE);
+ } else {
+ try {
+ Functions.installResource(Constants.EXAMPLE_CONFIG_FILE, Constants.CONFIG_FILE);
+ log.log(Level.FINE, "Installing default config file: {0}", Constants.CONFIG_FILE);
+ } catch (IOException e) {
+ throw new ConfigurationException("Unable to write example configuration to " + Constants.CONFIG_FILE, e);
+ }
+ }
+ }
+
+ private void generateBashCompletion() {
+ if (configuration == null) {
+ log.log(Level.FINER, "Not writing Bash completion helper files. In order to generate these files please run some command which requires configuration.");
+ } else {
+ try {
+ File dir = new File(Constants.DIR, "bash-completion");
+ dir.mkdir();
+ writeBashCompletionHelperFile(configuration.getDatabases(), new File(dir, "databases"));
+ writeBashCompletionHelperFile(configuration.getAllFormatters(), new File(dir, "formatters"));
+ writeBashCompletionHelperFileForFormatterProperties(new File(dir, "formatter-properties"));
+ } catch (Exception e) {
+ log.log(Level.WARNING, "Unable to generate Bash completion helper files", e);
+ }
+ }
+ }
+
+ private void writeBashCompletionHelperFile(Collection<? extends NameIdentified> items, File target) throws FileNotFoundException {
+ if (Constants.CONFIG_FILE.lastModified() > target.lastModified()) {
+ try (PrintWriter fw = new PrintWriter(target)) {
+ for (NameIdentified dd : items) {
+ fw.println(dd.getName());
+ }
+ fw.close();
+ log.log(Level.FINE, "Bash completion helper file was written: {0}", target);
+ }
+ } else {
+ log.log(Level.FINER, "Not writing Bash completion helper file: {0} because configuration {1} has not been changed", new Object[]{target, Constants.CONFIG_FILE});
+ }
+ }
+
+ private void writeBashCompletionHelperFileForFormatterProperties(File formattersDir) throws ClassNotFoundException, FileNotFoundException {
+ if (Constants.CONFIG_FILE.lastModified() > formattersDir.lastModified()) {
+ // TODO: delete old directory
+ formattersDir.mkdir();
+ for (FormatterDefinition fd : configuration.getAllFormatters()) {
+ File formatterDir = new File(formattersDir, fd.getName());
+ formatterDir.mkdir();
+
+ Class<Formatter> formatterClass = (Class<Formatter>) Class.forName(fd.getClassName());
+ List<Class<? extends Formatter>> hierarchy = Functions.getClassHierarchy(formatterClass, Formatter.class);
+ Collections.reverse(hierarchy);
+ for (Class<? extends Formatter> c : hierarchy) {
+ for (PropertyDeclaration p : Functions.getPropertyDeclarations(c)) {
+ File propertyDir = new File(formatterDir, p.name());
+ propertyDir.mkdir();
+ File choicesFile = new File(propertyDir, "choices");
+ try (PrintWriter fw = new PrintWriter(choicesFile)) {
+ // TODO: refactor, move
+ if (p.type() == Boolean.class) {
+ fw.println("true");
+ fw.println("false");
+ }
+ }
+ }
+ }
+ }
+ log.log(Level.FINE, "Bash completion helper files was written in: {0}", formattersDir);
+ } else {
+ log.log(Level.FINER, "Not writing Bash completion helper directory: {0} because configuration {1} has not been changed", new Object[]{formattersDir, Constants.CONFIG_FILE});
+ }
+
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/ColorfulPrintWriter.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,358 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.util.EnumSet;
+
+/**
+ * PrintWriter with convenience methods for printing color and formatted text.
+ *
+ * Uses ANSI Escape Sequences.
+ * See: http://www.tldp.org/HOWTO/Bash-Prompt-HOWTO/x329.html
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class ColorfulPrintWriter extends PrintWriter {
+
+ public enum TerminalColor {
+
+ Black(30, 40),
+ Red(31, 41),
+ Green(32, 42),
+ Yellow(33, 43),
+ Blue(34, 44),
+ Magenta(35, 45),
+ Cyan(36, 46),
+ White(37, 47);
+ private final int foregroundCode;
+ private final int backgroundCode;
+
+ private TerminalColor(int foregroundCode, int backgroundCode) {
+ this.foregroundCode = foregroundCode;
+ this.backgroundCode = backgroundCode;
+ }
+
+ public int getForegroundCode() {
+ return foregroundCode;
+ }
+
+ public int getBackgroundCode() {
+ return backgroundCode;
+ }
+ }
+
+ public enum TerminalStyle {
+
+ Reset(0),
+ Bright(1),
+ Dim(2),
+ Underscore(4),
+ Blink(5),
+ Reverse(7),
+ Hidden(8);
+ private int code;
+
+ private TerminalStyle(int code) {
+ this.code = code;
+ }
+
+ public int getCode() {
+ return code;
+ }
+ }
+ private final boolean COLOR_ENABLED;
+ private boolean colorful = true;
+
+ public void setStyle(TerminalStyle style) {
+ setStyle(EnumSet.of(style));
+ }
+
+ public void setStyle(EnumSet<TerminalStyle> styles) {
+ printCodes(getStyleCodes(styles));
+ }
+
+ private static int[] getStyleCodes(EnumSet<TerminalStyle> styles) {
+ int[] array = new int[styles.size()];
+ int i = 0;
+ for (TerminalStyle s : styles) {
+ array[i++] = s.getCode();
+ }
+ return array;
+ }
+
+ /**
+ * Print (usually audible) bell code (\007, \a, ^G)
+ */
+ public void bell() {
+ print("\007");
+ }
+
+ /**
+ * Eat the last character
+ */
+ public void backspace() {
+ print("\b");
+ }
+
+ /**
+ * Eat n last characters
+ *
+ * @param count n
+ */
+ public void backspace(int count) {
+ for (int i = 0; i < count; i++) {
+ backspace();
+ }
+ }
+
+ /**
+ * With 100 ms delay and all colors.
+ *
+ * @see #printRainbow(java.lang.String, int,
+ * info.globalcode.sql.dk.ColorfulPrintWriter.TerminalColor[])
+ */
+ public void printRainbow(String string) {
+ printRainbow(string, 100);
+ }
+
+ /**
+ * With all colors.
+ *
+ * @see #printRainbow(java.lang.String, int,
+ * info.globalcode.sql.dk.ColorfulPrintWriter.TerminalColor[])
+ */
+ public void printRainbow(String string, int delay) {
+ printRainbow(string, delay, TerminalColor.values());
+ }
+
+ /**
+ * Prints rainbow text – (re)writes same text subsequently in given colors and then in default
+ * color.
+ *
+ * @param string text to be printed, should not contain \n new line (then rainbow does not work
+ * – use println() after printRainbow() instead)
+ * @param delay delay between rewrites
+ * @param colors list of colors to be used
+ */
+ public void printRainbow(String string, int delay, TerminalColor... colors) {
+ for (TerminalColor c : colors) {
+ print(c, string);
+ try {
+ Thread.sleep(delay);
+ } catch (InterruptedException e) {
+ // no time to sleep
+ break;
+ }
+ backspace(string.length());
+ flush();
+ }
+ print(string);
+ }
+
+ public void setForegroundColor(TerminalColor color) {
+ printCodes(color.getForegroundCode());
+ }
+
+ public void setBackgroundColor(TerminalColor color) {
+ printCodes(color.getBackgroundCode());
+ }
+
+ public void print(TerminalColor foregroundColor, String string) {
+ setForegroundColor(foregroundColor);
+ print(string);
+ resetAll();
+ }
+
+ public void println(TerminalColor foregroundColor, String string) {
+ print(foregroundColor, string);
+ println();
+ }
+
+ public void print(TerminalColor foregroundColor, TerminalColor backgroundColor, String string) {
+ setForegroundColor(foregroundColor);
+ setBackgroundColor(backgroundColor);
+ print(string);
+ resetAll();
+ }
+
+ public void println(TerminalColor foregroundColor, TerminalColor backgroundColor, String string) {
+ print(foregroundColor, backgroundColor, string);
+ println();
+ }
+
+ public void print(TerminalColor foregroundColor, TerminalColor backgroundColor, EnumSet<TerminalStyle> styles, String string) {
+ setForegroundColor(foregroundColor);
+ setBackgroundColor(backgroundColor);
+ setStyle(styles);
+ print(string);
+ resetAll();
+ }
+
+ public void println(TerminalColor foregroundColor, TerminalColor backgroundColor, EnumSet<TerminalStyle> styles, String string) {
+ print(foregroundColor, backgroundColor, styles, string);
+ println();
+ }
+
+ public void print(TerminalColor foregroundColor, TerminalColor backgroundColor, TerminalStyle style, String string) {
+ print(foregroundColor, backgroundColor, EnumSet.of(style), string);
+ }
+
+ public void println(TerminalColor foregroundColor, TerminalColor backgroundColor, TerminalStyle style, String string) {
+ print(foregroundColor, backgroundColor, style, string);
+ println();
+ }
+
+ public void print(TerminalColor foregroundColor, EnumSet<TerminalStyle> styles, String string) {
+ setForegroundColor(foregroundColor);
+ setStyle(styles);
+ print(string);
+ resetAll();
+ }
+
+ public void println(TerminalColor foregroundColor, EnumSet<TerminalStyle> styles, String string) {
+ print(foregroundColor, styles, string);
+ println();
+ }
+
+ public void print(TerminalColor foregroundColor, TerminalStyle style, String string) {
+ print(foregroundColor, EnumSet.of(style), string);
+ }
+
+ public void println(TerminalColor foregroundColor, TerminalStyle style, String string) {
+ print(foregroundColor, style, string);
+ println();
+ }
+
+ public void print(EnumSet<TerminalStyle> styles, String string) {
+ setStyle(styles);
+ print(string);
+ resetAll();
+ }
+
+ public void println(EnumSet<TerminalStyle> styles, String string) {
+ print(styles, string);
+ println();
+ }
+
+ public void print(TerminalStyle style, String string) {
+ print(EnumSet.of(style), string);
+ }
+
+ public void println(TerminalStyle style, String string) {
+ print(style, string);
+ println();
+ }
+
+ public void resetAll() {
+ printCodes(TerminalStyle.Reset.code);
+ }
+
+ private void printCodes(int... codes) {
+ if (COLOR_ENABLED && colorful) {
+ print("\033[");
+ for (int i = 0; i < codes.length; i++) {
+ print(codes[i]);
+ if (i < codes.length - 1 && codes.length > 1) {
+ print(";");
+ }
+ }
+ print("m");
+ }
+ }
+
+ /**
+ * Colors can be switched on/off during usage of this writer.
+ *
+ * @return whether colors are currently turned on
+ * @see #isColorEnabled()
+ */
+ public boolean isColorful() {
+ return colorful;
+ }
+
+ /**
+ * Collors might be definitively disabled in constructor. If not, they can be turned on/off
+ * during usage of this writer by {@linkplain #setColorful(boolean)}
+ *
+ * @return whether colors are allowed for this instance of this class
+ * @see #isColorful()
+ */
+ public boolean isColorEnabled() {
+ return COLOR_ENABLED;
+ }
+
+ /**
+ * @see #isColorful()
+ * @see #isColorEnabled()
+ */
+ public void setColorful(boolean colorful) {
+ this.colorful = colorful;
+ }
+
+ public ColorfulPrintWriter(File file) throws FileNotFoundException {
+ super(file);
+ COLOR_ENABLED = true;
+ }
+
+ public ColorfulPrintWriter(OutputStream out) {
+ super(out);
+ COLOR_ENABLED = true;
+ }
+
+ public ColorfulPrintWriter(String fileName) throws FileNotFoundException {
+ super(fileName);
+ COLOR_ENABLED = true;
+ }
+
+ public ColorfulPrintWriter(Writer out) {
+ super(out);
+ COLOR_ENABLED = true;
+ }
+
+ public ColorfulPrintWriter(File file, String csn) throws FileNotFoundException, UnsupportedEncodingException {
+ super(file, csn);
+ COLOR_ENABLED = true;
+ }
+
+ /**
+ * @param colorEnabled colors might be definitively disabled by this option – this might be more
+ * optimalizable than dynamic turning off colors by {@linkplain #setColorful(boolean)} which is
+ * not definitive (colors can be turned on during live of this instance). This might be useful
+ * if you need an instance of this class but don't need colors at all.
+ */
+ public ColorfulPrintWriter(OutputStream out, boolean autoFlush, boolean colorEnabled) {
+ super(out, autoFlush);
+ COLOR_ENABLED = colorEnabled;
+ }
+
+ public ColorfulPrintWriter(String fileName, String csn) throws FileNotFoundException, UnsupportedEncodingException {
+ super(fileName, csn);
+ COLOR_ENABLED = true;
+ }
+
+ public ColorfulPrintWriter(Writer out, boolean autoFlush) {
+ super(out, autoFlush);
+ COLOR_ENABLED = true;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/Constants.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,44 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk;
+
+import java.io.File;
+
+/**
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class Constants {
+
+ public static final String PROGRAM_NAME = "SQL-DK";
+ public static final String JAVA_PACKAGE = Constants.class.getPackage().getName();
+ public static final String WEBSITE = "https://sql-dk.globalcode.info/";
+ public static final String LICENSE_FILE = "info/globalcode/sql/dk/license.txt";
+ public static final String VERSION_FILE = "info/globalcode/sql/dk/version.txt";
+ public static final String HELP_FILE = "info/globalcode/sql/dk/help.txt";
+ private static final File HOME_DIR = new File(System.getProperty("user.home"));
+ /**
+ * Directory where config and log files are stored.
+ */
+ public static final File DIR = new File(HOME_DIR, ".sql-dk"); // bash-completion:dir
+ public static final File CONFIG_FILE = new File(DIR, "config.xml");
+ public static final String EXAMPLE_CONFIG_FILE = "info/globalcode/sql/dk/example-config.xml";
+
+ private Constants() {
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/DKException.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,41 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk;
+
+/**
+ * TODO: GEC
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class DKException extends Exception {
+
+ public DKException() {
+ }
+
+ public DKException(String message) {
+ super(message);
+ }
+
+ public DKException(Throwable cause) {
+ super(cause);
+ }
+
+ public DKException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/DatabaseConnection.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,192 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk;
+
+import static info.globalcode.sql.dk.jmx.ConnectionManagement.incrementCounter;
+import static info.globalcode.sql.dk.jmx.ConnectionManagement.resetCounter;
+import info.globalcode.sql.dk.batch.Batch;
+import info.globalcode.sql.dk.batch.BatchException;
+import info.globalcode.sql.dk.configuration.DatabaseDefinition;
+import info.globalcode.sql.dk.configuration.Loader;
+import info.globalcode.sql.dk.configuration.Properties;
+import info.globalcode.sql.dk.formatting.ColumnsHeader;
+import info.globalcode.sql.dk.formatting.Formatter;
+import info.globalcode.sql.dk.jmx.ConnectionManagement;
+import info.globalcode.sql.dk.jmx.ConnectionManagement.COUNTER;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Represents connected database. Is derived from {@linkplain DatabaseDefinition}.
+ * Wraps {@linkplain Connection}.
+ *
+ * Is responsible for executing {@linkplain SQLCommand} and passing results to the
+ * {@linkplain Formatter}.
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class DatabaseConnection implements AutoCloseable {
+
+ private static final Logger log = Logger.getLogger(DatabaseConnection.class.getName());
+ public static final String JDBC_PROPERTY_USER = "user";
+ public static final String JDBC_PROPERTY_PASSWORD = "password";
+ private final DatabaseDefinition databaseDefinition;
+ private final Connection connection;
+ private final Properties properties;
+ /**
+ * Could be null = JMX is disabled → must check, see functions in
+ * {@linkplain ConnectionManagement}
+ */
+ private final ConnectionManagement connectionMBean;
+
+ /**
+ *
+ * @param databaseDefinition DB url, name, password etc.
+ * @param properties additional properties from CLI
+ * @param connectionMBean JMX management bean | null = disabled JMX reporting
+ * @throws SQLException
+ */
+ public DatabaseConnection(DatabaseDefinition databaseDefinition, Properties properties, ConnectionManagement connectionMBean) throws SQLException {
+ this.databaseDefinition = databaseDefinition;
+ this.properties = properties;
+ this.connectionMBean = connectionMBean;
+ this.connection = Loader.jdbcConnect(databaseDefinition, properties);
+ }
+
+ public void executeQuery(SQLCommand sqlCommand, Formatter formatter) throws SQLException {
+ formatter.writeStartBatch();
+ formatter.writeStartDatabase(databaseDefinition);
+ formatter.writeStartStatement();
+ formatter.writeQuery(sqlCommand.getQuery());
+ formatter.writeParameters(sqlCommand.getParameters());
+ processCommand(sqlCommand, formatter);
+ formatter.writeEndStatement();
+ formatter.writeEndDatabase();
+ formatter.writeEndBatch();
+ }
+
+ public void executeBatch(Batch batch, Formatter formatter) throws SQLException, BatchException {
+ formatter.writeStartBatch();
+ formatter.writeStartDatabase(databaseDefinition);
+ while (batch.hasNext()) {
+ SQLCommand sqlCommand = batch.next();
+ formatter.writeStartStatement();
+ formatter.writeQuery(sqlCommand.getQuery());
+ formatter.writeParameters(sqlCommand.getParameters());
+ processCommand(sqlCommand, formatter);
+ formatter.writeEndStatement();
+ }
+ formatter.writeEndDatabase();
+ formatter.writeEndBatch();
+ }
+
+ private void processCommand(SQLCommand sqlCommand, Formatter formatter) throws SQLException {
+ incrementCounter(connectionMBean, COUNTER.COMMAND);
+ resetCounter(connectionMBean, COUNTER.RECORD_CURRENT);
+
+ try (PreparedStatement ps = sqlCommand.prepareStatement(connection)) {
+ log.log(Level.FINE, "Statement prepared");
+ sqlCommand.parametrize(ps);
+
+ boolean isRS = ps.execute();
+ log.log(Level.FINE, "Statement executed");
+ if (isRS) {
+ try (ResultSet rs = ps.getResultSet()) {
+ processResultSet(rs, formatter);
+ }
+ } else {
+ processUpdateResult(ps, formatter);
+ }
+ logWarnings(ps);
+
+ while (ps.getMoreResults() || ps.getUpdateCount() > -1) {
+ ResultSet rs = ps.getResultSet();
+ if (rs == null) {
+ processUpdateResult(ps, formatter);
+ } else {
+ processResultSet(rs, formatter);
+ rs.close();
+ }
+ logWarnings(ps);
+ }
+ }
+ }
+
+ private void processUpdateResult(PreparedStatement ps, Formatter formatter) throws SQLException {
+ formatter.writeUpdatesResult(ps.getUpdateCount());
+ }
+
+ private void processResultSet(ResultSet rs, Formatter formatter) throws SQLException {
+ formatter.writeStartResultSet(new ColumnsHeader(rs.getMetaData()));
+
+ int columnCount = rs.getMetaData().getColumnCount();
+
+ while (rs.next()) {
+ incrementCounter(connectionMBean, COUNTER.RECORD_CURRENT);
+ incrementCounter(connectionMBean, COUNTER.RECORD_TOTAL);
+
+ formatter.writeStartRow();
+
+ for (int i = 1; i <= columnCount; i++) {
+ formatter.writeColumnValue(rs.getObject(i));
+ }
+
+ formatter.writeEndRow();
+ }
+
+ formatter.writeEndResultSet();
+ }
+
+ private void logWarnings(PreparedStatement ps) throws SQLException {
+ SQLWarning w = ps.getWarnings();
+ while (w != null) {
+ log.log(Level.WARNING, "SQL: {0}", w.getLocalizedMessage());
+ w = w.getNextWarning();
+ }
+ ps.clearWarnings();
+ }
+
+ /**
+ * Tests if this connection is live.
+ *
+ * @return true if test was successful
+ * @throws SQLException if test fails
+ */
+ public boolean test() throws SQLException {
+ connection.getAutoCommit();
+ return true;
+ }
+
+ public String getProductName() throws SQLException {
+ return connection.getMetaData().getDatabaseProductName();
+ }
+
+ public String getProductVersion() throws SQLException {
+ return connection.getMetaData().getDatabaseProductVersion();
+ }
+
+ @Override
+ public void close() throws SQLException {
+ connection.close();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/Functions.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,254 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk;
+
+import info.globalcode.sql.dk.configuration.NameIdentified;
+import info.globalcode.sql.dk.configuration.PropertyDeclaration;
+import info.globalcode.sql.dk.configuration.PropertyDeclarations;
+import info.globalcode.sql.dk.formatting.Formatter;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class Functions {
+
+ private static final String NBSP = " ";
+ private static final Pattern WHITESPACE_TO_REPLACE = Pattern.compile("\\n|\\r|\\t|" + NBSP);
+
+ private Functions() {
+ }
+
+ public static boolean equalz(Object a, Object b) {
+ return a == null ? b == null : a.equals(b);
+ }
+
+ /**
+ *
+ * @param text String to be examinated
+ * @param trim whether text should be trimmed before examination
+ * @return whether text is not empty and one or more characters long (after prospective trim)
+ */
+ public static boolean isEmpty(String text, boolean trim) {
+ if (text == null) {
+ return true;
+ } else {
+ if (trim) {
+ text = text.trim();
+ }
+ return text.isEmpty();
+ }
+ }
+
+ /**
+ * @see #isEmpty(java.lang.String, boolean)
+ */
+ public static boolean isNotEmpty(String text, boolean trim) {
+ return !isEmpty(text, trim);
+ }
+
+ public boolean isEmpty(Collection c) {
+ return c == null || c.isEmpty();
+ }
+
+ public boolean isNotEmpty(Collection c) {
+ return !isEmpty(c);
+ }
+
+ public boolean isEmpty(Map m) {
+ return m == null || m.isEmpty();
+ }
+
+ public boolean isNotEmpty(Map m) {
+ return !isEmpty(m);
+ }
+
+ /**
+ * @return empty collection if given one is null | or the original one
+ */
+ public static <T> Collection<T> notNull(Collection<T> c) {
+ if (c == null) {
+ return Collections.emptyList();
+ } else {
+ return c;
+ }
+ }
+
+ public static <T extends NameIdentified> T findByName(Collection<T> collection, String name) {
+ for (T element : notNull(collection)) {
+ if (element != null && equalz(element.getName(), name)) {
+ return element;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Copy file from Java resources to file system.
+ */
+ public static void installResource(String resourceName, File target) throws IOException {
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(Functions.class.getClassLoader().getResourceAsStream(resourceName)))) {
+ try (PrintWriter writer = new PrintWriter(target)) {
+ while (true) {
+ String line = reader.readLine();
+ if (line == null) {
+ break;
+ } else {
+ writer.println(line);
+ }
+ }
+ }
+ }
+ }
+
+ public static String rpad(String s, int n) {
+ if (n > 0) {
+ return String.format("%1$-" + n + "s", s);
+ } else {
+ return s;
+ }
+ }
+
+ public static String lpad(String s, int n) {
+ if (n > 0) {
+ return String.format("%1$" + n + "s", s);
+ } else {
+ return s;
+ }
+ }
+
+ public static String repeat(char ch, int count) {
+ char[] array = new char[count];
+ Arrays.fill(array, ch);
+ return new String(array);
+ }
+ private final static char[] HEX_ALPHABET = "0123456789abcdef".toCharArray();
+
+ public static String toHex(byte[] bytes) {
+ char[] hexChars = new char[bytes.length * 2];
+ for (int j = 0; j < bytes.length; j++) {
+ int v = bytes[j] & 0xFF;
+ hexChars[j * 2] = HEX_ALPHABET[v >>> 4];
+ hexChars[j * 2 + 1] = HEX_ALPHABET[v & 0x0F];
+ }
+ return new String(hexChars);
+ }
+
+ public static String readString(InputStream in) throws IOException {
+ try (BufferedReader br = new BufferedReader(new InputStreamReader(in))) {
+ StringBuilder result = new StringBuilder();
+ for (String line = br.readLine(); line != null; line = br.readLine()) {
+ result.append(line);
+ result.append('\n');
+ }
+ return result.toString();
+ }
+ }
+
+ /**
+ * @param <P> type of the last parent
+ * @param <T> type of the examined class
+ * @param type examined class
+ * @param lastParent the last parent type to stop at
+ * @return list of types starting with <code>type</code> and ending with <code>lastParent</code>
+ */
+ public static <P, T extends P> List<Class<? extends P>> getClassHierarchy(Class<T> type, Class<P> lastParent) {
+ List<Class<? extends P>> hierarchy = new ArrayList<>();
+
+ for (Class current = type; current != null && lastParent.isAssignableFrom(current); current = current.getSuperclass()) {
+ hierarchy.add(current);
+ }
+
+ return hierarchy;
+ }
+
+ public static PropertyDeclaration[] getPropertyDeclarations(Class<? extends Formatter> formatterClass) {
+ PropertyDeclarations properties = formatterClass.getAnnotation(PropertyDeclarations.class);
+
+ if (properties == null) {
+ PropertyDeclaration p = formatterClass.getAnnotation(PropertyDeclaration.class);
+ return p == null ? new PropertyDeclaration[]{} : new PropertyDeclaration[]{p};
+ } else {
+ return properties.value();
+ }
+ }
+
+ /**
+ * TODO: support background or styles and move to ColorfulPrintWriter
+ *
+ * @param out
+ * @param valueString
+ * @param basicColor
+ * @param escapeColor
+ */
+ public static void printValueWithWhitespaceReplaced(ColorfulPrintWriter out, String valueString, ColorfulPrintWriter.TerminalColor basicColor, ColorfulPrintWriter.TerminalColor escapeColor) {
+
+ Matcher m = WHITESPACE_TO_REPLACE.matcher(valueString);
+
+ int start = 0;
+
+ while (m.find(start)) {
+
+ printColorOrNot(out, basicColor, valueString.substring(start, m.start()));
+
+ switch (m.group()) {
+ case "\n":
+ out.print(escapeColor, "↲");
+ break;
+ case "\r":
+ out.print(escapeColor, "⏎");
+ break;
+ case "\t":
+ out.print(escapeColor, "↹");
+ break;
+ case NBSP:
+ out.print(escapeColor, "⎵");
+ break;
+ default:
+ throw new IllegalStateException("Unexpected whitespace token: „" + m.group() + "“");
+ }
+
+ start = m.end();
+ }
+
+ printColorOrNot(out, basicColor, valueString.substring(start, valueString.length()));
+ }
+
+ private static void printColorOrNot(ColorfulPrintWriter out, ColorfulPrintWriter.TerminalColor color, String text) {
+ if (color == null) {
+ out.print(text);
+ } else {
+ out.print(color, text);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/InfoLister.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,673 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk;
+
+import info.globalcode.sql.dk.configuration.CommandArgument;
+import info.globalcode.sql.dk.configuration.Configuration;
+import info.globalcode.sql.dk.configuration.ConfigurationException;
+import info.globalcode.sql.dk.configuration.ConfigurationProvider;
+import info.globalcode.sql.dk.configuration.DatabaseDefinition;
+import info.globalcode.sql.dk.configuration.FormatterDefinition;
+import info.globalcode.sql.dk.configuration.Properties;
+import info.globalcode.sql.dk.configuration.Property;
+import info.globalcode.sql.dk.configuration.PropertyDeclaration;
+import info.globalcode.sql.dk.configuration.TunnelDefinition;
+import info.globalcode.sql.dk.formatting.ColumnsHeader;
+import info.globalcode.sql.dk.formatting.CommonProperties;
+import info.globalcode.sql.dk.formatting.FakeSqlArray;
+import info.globalcode.sql.dk.formatting.Formatter;
+import info.globalcode.sql.dk.formatting.FormatterContext;
+import info.globalcode.sql.dk.formatting.FormatterException;
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.sql.Array;
+import java.sql.Driver;
+import java.sql.DriverManager;
+import java.sql.DriverPropertyInfo;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.ServiceLoader;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+import javax.sql.rowset.RowSetMetaDataImpl;
+
+/**
+ * Displays info like help, version etc.
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class InfoLister {
+
+ private static final Logger log = Logger.getLogger(InfoLister.class.getName());
+ /**
+ * Fake database name for output formatting
+ */
+ public static final String CONFIG_DB_NAME = "sqldk_configuration";
+ private final PrintStream out;
+ private final ConfigurationProvider configurationProvider;
+ private final CLIOptions options;
+ private Formatter formatter;
+
+ public InfoLister(PrintStream out, ConfigurationProvider configurationProvider, CLIOptions options) {
+ this.out = out;
+ this.configurationProvider = configurationProvider;
+ this.options = options;
+ }
+
+ public void showInfo() throws ConfigurationException, FormatterException {
+ EnumSet<InfoType> commands = options.getShowInfo();
+
+ boolean formattinNeeded = false;
+
+ for (InfoType infoType : commands) {
+ switch (infoType) {
+ case CONNECTION:
+ case JDBC_DRIVERS:
+ case JDBC_PROPERTIES:
+ case DATABASES:
+ case FORMATTERS:
+ case FORMATTER_PROPERTIES:
+ case TYPES:
+ case JAVA_PROPERTIES:
+ case ENVIRONMENT_VARIABLES:
+ formattinNeeded = true;
+ break;
+ }
+ }
+
+ if (formattinNeeded) {
+ try (Formatter f = getFormatter()) {
+ formatter = f;
+ formatter.writeStartBatch();
+ DatabaseDefinition dd = new DatabaseDefinition();
+ dd.setName(CONFIG_DB_NAME);
+ formatter.writeStartDatabase(dd);
+ showInfos(commands);
+ formatter.writeEndDatabase();
+ formatter.writeEndBatch();
+ formatter.close();
+ }
+ } else {
+ showInfos(commands);
+ }
+ }
+
+ private void showInfos(EnumSet<InfoType> commands) throws ConfigurationException, FormatterException {
+ for (InfoType infoType : commands) {
+ infoType.showInfo(this);
+ }
+ }
+
+ private void listJavaProperties() throws FormatterException, ConfigurationException {
+ ColumnsHeader header = constructHeader(new HeaderField("name", SQLType.VARCHAR), new HeaderField("value", SQLType.VARCHAR));
+ List<Object[]> data = new ArrayList<>();
+ for (Entry<Object, Object> e : System.getProperties().entrySet()) {
+ data.add(new Object[]{e.getKey(), e.getValue()});
+ }
+ printTable(formatter, header, "-- Java system properties", null, data, 0);
+ }
+
+ private void listEnvironmentVariables() throws FormatterException, ConfigurationException {
+ ColumnsHeader header = constructHeader(new HeaderField("name", SQLType.VARCHAR), new HeaderField("value", SQLType.VARCHAR));
+ List<Object[]> data = new ArrayList<>();
+ for (Entry<String, String> e : System.getenv().entrySet()) {
+ data.add(new Object[]{e.getKey(), e.getValue()});
+ }
+ printTable(formatter, header, "-- environment variables", null, data, 0);
+ }
+
+ private void listFormatters() throws ConfigurationException, FormatterException {
+ ColumnsHeader header = constructHeader(
+ new HeaderField("name", SQLType.VARCHAR),
+ new HeaderField("built_in", SQLType.BOOLEAN),
+ new HeaderField("default", SQLType.BOOLEAN),
+ new HeaderField("class_name", SQLType.VARCHAR),
+ new HeaderField("valid", SQLType.BOOLEAN));
+ List<Object[]> data = new ArrayList<>();
+
+ String defaultFormatter = configurationProvider.getConfiguration().getDefaultFormatter();
+ defaultFormatter = defaultFormatter == null ? Configuration.DEFAULT_FORMATTER : defaultFormatter;
+
+ for (FormatterDefinition fd : configurationProvider.getConfiguration().getBuildInFormatters()) {
+ data.add(new Object[]{fd.getName(), true, defaultFormatter.equals(fd.getName()), fd.getClassName(), isInstantiable(fd)});
+ }
+
+ for (FormatterDefinition fd : configurationProvider.getConfiguration().getFormatters()) {
+ data.add(new Object[]{fd.getName(), false, defaultFormatter.equals(fd.getName()), fd.getClassName(), isInstantiable(fd)});
+ }
+
+ printTable(formatter, header, "-- configured and built-in output formatters", null, data);
+ }
+
+ private boolean isInstantiable(FormatterDefinition fd) {
+ try {
+ try (ByteArrayOutputStream testStream = new ByteArrayOutputStream()) {
+ fd.getInstance(new FormatterContext(testStream, new Properties(0)));
+ return true;
+ }
+ } catch (Exception e) {
+ log.log(Level.SEVERE, "Unable to create an instance of formatter: " + fd.getName(), e);
+ return false;
+ }
+ }
+
+ private void listFormatterProperties() throws FormatterException, ConfigurationException {
+ for (String formatterName : options.getFormatterNamesToListProperties()) {
+ listFormatterProperties(formatterName);
+ }
+ }
+
+ private void listFormatterProperties(String formatterName) throws FormatterException, ConfigurationException {
+ FormatterDefinition fd = configurationProvider.getConfiguration().getFormatter(formatterName);
+ try {
+
+ // currently only for debugging purposes
+ // TODO: introduce --info-lister-property or generic filtering capability in printTable() ?
+ boolean printDeclaredIn = options.getFormatterProperties().getBoolean("InfoLister:print:declared_in", false);
+
+ List<HeaderField> headerFields = new ArrayList<>();
+ headerFields.add(new HeaderField("name", SQLType.VARCHAR));
+ headerFields.add(new HeaderField("type", SQLType.VARCHAR));
+ headerFields.add(new HeaderField("default", SQLType.VARCHAR));
+ headerFields.add(new HeaderField("description", SQLType.VARCHAR));
+ if (printDeclaredIn) {
+ headerFields.add(new HeaderField("declared_in", SQLType.VARCHAR));
+ }
+
+ ColumnsHeader header = constructHeader(headerFields.toArray(new HeaderField[0]));
+
+ Map<String, Object[]> data = new HashMap<>();
+ Class<Formatter> formatterClass = (Class<Formatter>) Class.forName(fd.getClassName());
+ List<Class<? extends Formatter>> hierarchy = Functions.getClassHierarchy(formatterClass, Formatter.class);
+ Collections.reverse(hierarchy);
+ hierarchy.stream().forEach((c) -> {
+ for (PropertyDeclaration p : Functions.getPropertyDeclarations(c)) {
+ data.put(p.name(), propertyDeclarationToRow(p, c, printDeclaredIn));
+ }
+ });
+
+ List<Parameter> parameters = new ArrayList<>();
+ parameters.add(new NamedParameter("formatter", formatterName, SQLType.VARCHAR));
+
+ printTable(formatter, header, "-- formatter properties", parameters, new ArrayList<>(data.values()));
+ } catch (ClassNotFoundException e) {
+ throw new ConfigurationException("Unable to find class " + fd.getClassName() + " of formatter" + fd.getName(), e);
+ }
+ }
+
+ private static Object[] propertyDeclarationToRow(PropertyDeclaration p, Class formatterClass, boolean printDeclaredIn) {
+ List list = new ArrayList();
+
+ list.add(p.name());
+ list.add(CommonProperties.getSimpleTypeName(p.type()));
+ list.add(p.defaultValue());
+ list.add(p.description());
+ if (printDeclaredIn) {
+ list.add(formatterClass.getName());
+ }
+
+ return list.toArray();
+ }
+
+ private void listTypes() throws FormatterException, ConfigurationException {
+ ColumnsHeader header = constructHeader(new HeaderField("name", SQLType.VARCHAR), new HeaderField("code", SQLType.INTEGER));
+ List<Object[]> data = new ArrayList<>();
+ for (SQLType sqlType : SQLType.values()) {
+ data.add(new Object[]{sqlType.name(), sqlType.getCode()});
+ }
+ printTable(formatter, header, "-- data types", null, data);
+ log.log(Level.INFO, "Type names in --types option are case insensitive");
+ }
+
+ private void listDatabases() throws ConfigurationException, FormatterException {
+ ColumnsHeader header = constructHeader(
+ new HeaderField("database_name", SQLType.VARCHAR),
+ new HeaderField("user_name", SQLType.VARCHAR),
+ new HeaderField("database_url", SQLType.VARCHAR));
+ List<Object[]> data = new ArrayList<>();
+
+ final List<DatabaseDefinition> configuredDatabases = configurationProvider.getConfiguration().getDatabases();
+ if (configuredDatabases.isEmpty()) {
+ log.log(Level.WARNING, "No databases are configured.");
+ } else {
+ for (DatabaseDefinition dd : configuredDatabases) {
+ data.add(new Object[]{dd.getName(), dd.getUserName(), dd.getUrl()});
+
+ final TunnelDefinition tunnel = dd.getTunnel();
+ if (tunnel != null) {
+ log.log(Level.INFO, "Tunnel command: {0}", tunnel.getCommand());
+ for (CommandArgument ca : Functions.notNull(tunnel.getArguments())) {
+ log.log(Level.INFO, "\targument: {0}/{1}", new Object[]{ca.getType(), ca.getValue()});
+ }
+ }
+
+ }
+ }
+
+ printTable(formatter, header, "-- configured databases", null, data);
+ }
+
+ private void listJdbcDrivers() throws FormatterException, ConfigurationException {
+ ColumnsHeader header = constructHeader(
+ new HeaderField("class", SQLType.VARCHAR),
+ new HeaderField("version", SQLType.VARCHAR),
+ new HeaderField("major", SQLType.INTEGER),
+ new HeaderField("minor", SQLType.INTEGER),
+ new HeaderField("jdbc_compliant", SQLType.BOOLEAN));
+ List<Object[]> data = new ArrayList<>();
+
+ final ServiceLoader<Driver> drivers = ServiceLoader.load(Driver.class);
+ for (Driver d : drivers) {
+ data.add(new Object[]{
+ d.getClass().getName(),
+ d.getMajorVersion() + "." + d.getMinorVersion(),
+ d.getMajorVersion(),
+ d.getMinorVersion(),
+ d.jdbcCompliant()
+ });
+ }
+
+ printTable(formatter, header, "-- discovered JDBC drivers (available on the CLASSPATH)", null, data);
+ }
+
+ private void listJdbcProperties() throws FormatterException, ConfigurationException {
+ for (String dbName : options.getDatabaseNamesToListProperties()) {
+ ColumnsHeader header = constructHeader(
+ new HeaderField("property_name", SQLType.VARCHAR),
+ new HeaderField("required", SQLType.BOOLEAN),
+ new HeaderField("choices", SQLType.ARRAY),
+ new HeaderField("configured_value", SQLType.VARCHAR),
+ new HeaderField("description", SQLType.VARCHAR));
+ List<Object[]> data = new ArrayList<>();
+
+ DatabaseDefinition dd = configurationProvider.getConfiguration().getDatabase(dbName);
+
+ Driver driver = findDriver(dd);
+
+ if (driver == null) {
+ log.log(Level.WARNING, "No JDBC driver was found for DB: {0} with URL: {1}", new Object[]{dd.getName(), dd.getUrl()});
+ } else {
+ log.log(Level.INFO, "For DB: {0} was found JDBC driver: {1}", new Object[]{dd.getName(), driver.getClass().getName()});
+
+ try {
+ DriverPropertyInfo[] propertyInfos = driver.getPropertyInfo(dd.getUrl(), dd.getProperties().getJavaProperties());
+
+ Set<String> standardProperties = new HashSet<>();
+
+ for (DriverPropertyInfo pi : propertyInfos) {
+ Array choices = new FakeSqlArray(pi.choices, SQLType.VARCHAR);
+ data.add(new Object[]{
+ pi.name,
+ pi.required,
+ choices.getArray() == null ? "" : choices,
+ pi.value == null ? "" : pi.value,
+ pi.description
+ });
+ standardProperties.add(pi.name);
+ }
+
+ for (Property p : dd.getProperties()) {
+ if (!standardProperties.contains(p.getName())) {
+ data.add(new Object[]{
+ p.getName(),
+ "",
+ "",
+ p.getValue(),
+ ""
+ });
+ log.log(Level.WARNING, "Your configuration contains property „{0}“ not declared by the JDBC driver.", p.getName());
+ }
+ }
+
+ } catch (SQLException e) {
+ log.log(Level.WARNING, "Error during getting property infos.", e);
+ }
+
+ List<Parameter> parameters = new ArrayList<>();
+ parameters.add(new NamedParameter("database", dbName, SQLType.VARCHAR));
+ parameters.add(new NamedParameter("driver_class", driver.getClass().getName(), SQLType.VARCHAR));
+ parameters.add(new NamedParameter("driver_major_version", driver.getMajorVersion(), SQLType.INTEGER));
+ parameters.add(new NamedParameter("driver_minor_version", driver.getMinorVersion(), SQLType.INTEGER));
+
+ printTable(formatter, header, "-- configured and configurable JDBC driver properties", parameters, data);
+ }
+ }
+
+ }
+
+ private Driver findDriver(DatabaseDefinition dd) {
+ final ServiceLoader<Driver> drivers = ServiceLoader.load(Driver.class);
+ for (Driver d : drivers) {
+ try {
+ if (d.acceptsURL(dd.getUrl())) {
+ return d;
+ }
+ } catch (SQLException e) {
+ log.log(Level.WARNING, "Error during finding JDBC driver for: " + dd.getName(), e);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Parallelism for connection testing – maximum concurrent database connections.
+ */
+ private static final int TESTING_THREAD_COUNT = 64;
+ /**
+ * Time limit for all connection testing threads – particular timeouts per connection will be
+ * much smaller.
+ */
+ private static final long TESTING_AWAIT_LIMIT = 1;
+ private static final TimeUnit TESTING_AWAIT_UNIT = TimeUnit.DAYS;
+
+ private void testConnections() throws FormatterException, ConfigurationException {
+ ColumnsHeader header = constructHeader(
+ new HeaderField("database_name", SQLType.VARCHAR),
+ new HeaderField("configured", SQLType.BOOLEAN),
+ new HeaderField("connected", SQLType.BOOLEAN),
+ new HeaderField("product_name", SQLType.VARCHAR),
+ new HeaderField("product_version", SQLType.VARCHAR));
+
+ log.log(Level.FINE, "Testing DB connections in {0} threads", TESTING_THREAD_COUNT);
+
+ ExecutorService es = Executors.newFixedThreadPool(TESTING_THREAD_COUNT);
+
+ final Formatter currentFormatter = formatter;
+
+ printHeader(currentFormatter, header, "-- database configuration and connectivity test", null);
+
+ for (final String dbName : options.getDatabaseNamesToTest()) {
+ preloadDriver(dbName);
+ }
+
+ for (final String dbName : options.getDatabaseNamesToTest()) {
+ es.submit(() -> {
+ final Object[] row = testConnection(dbName);
+ synchronized (currentFormatter) {
+ printRow(currentFormatter, row);
+ }
+ }
+ );
+ }
+
+ es.shutdown();
+
+ try {
+ log.log(Level.FINEST, "Waiting for test results: {0} {1}", new Object[]{TESTING_AWAIT_LIMIT, TESTING_AWAIT_UNIT.name()});
+ boolean finished = es.awaitTermination(TESTING_AWAIT_LIMIT, TESTING_AWAIT_UNIT);
+ if (finished) {
+ log.log(Level.FINEST, "All testing threads finished in time limit.");
+ } else {
+ throw new FormatterException("Exceeded total time limit for test threads – this should never happen");
+ }
+ } catch (InterruptedException e) {
+ throw new FormatterException("Interrupted while waiting for test results", e);
+ }
+
+ printFooter(currentFormatter);
+ }
+
+ /**
+ * JDBC driver classes should be preloaded in single thread to avoid deadlocks while doing
+ * {@linkplain DriverManager#registerDriver(java.sql.Driver)} during parallel connections.
+ *
+ * @param dbName
+ */
+ private void preloadDriver(String dbName) {
+ try {
+ DatabaseDefinition dd = configurationProvider.getConfiguration().getDatabase(dbName);
+ Driver driver = findDriver(dd);
+ if (driver == null) {
+ log.log(Level.WARNING, "No Driver found for DB: {0}", dbName);
+ } else {
+ log.log(Level.FINEST, "Driver preloading for DB: {0} was successfull", dbName);
+ }
+ } catch (Exception e) {
+ LogRecord r = new LogRecord(Level.WARNING, "Failed to preload the Driver for DB: {0}");
+ r.setParameters(new Object[]{dbName});
+ r.setThrown(e);
+ log.log(r);
+ }
+ }
+
+ private Object[] testConnection(String dbName) {
+ log.log(Level.FINE, "Testing connection to database: {0}", dbName);
+
+ boolean succesfullyConnected = false;
+ boolean succesfullyConfigured = false;
+ String productName = null;
+ String productVersion = null;
+
+ try {
+ DatabaseDefinition dd = configurationProvider.getConfiguration().getDatabase(dbName);
+ log.log(Level.FINE, "Database definition was loaded from configuration");
+ succesfullyConfigured = true;
+ try (DatabaseConnection dc = dd.connect(options.getDatabaseProperties())) {
+ succesfullyConnected = dc.test();
+ productName = dc.getProductName();
+ productVersion = dc.getProductVersion();
+ }
+ log.log(Level.FINE, "Database connection test was successful");
+ } catch (ConfigurationException | SQLException | RuntimeException e) {
+ log.log(Level.SEVERE, "Error during testing connection " + dbName, e);
+ }
+
+ return new Object[]{dbName, succesfullyConfigured, succesfullyConnected, productName, productVersion};
+ }
+
+ private void printResource(String fileName) {
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(getClass().getClassLoader().getResourceAsStream(fileName)))) {
+ while (true) {
+ String line = reader.readLine();
+ if (line == null) {
+ break;
+ } else {
+ println(line);
+ }
+ }
+ } catch (Exception e) {
+ log.log(Level.SEVERE, "Unable to print this info. Please see our website for it: " + Constants.WEBSITE, e);
+ }
+ }
+
+ private void println(String line) {
+ out.println(line);
+ }
+
+ private void printTable(Formatter formatter, ColumnsHeader header, String sql, List<Parameter> parameters, List<Object[]> data) throws ConfigurationException, FormatterException {
+ printTable(formatter, header, sql, parameters, data, null);
+ }
+
+ private void printTable(Formatter formatter, ColumnsHeader header, String sql, List<Parameter> parameters, List<Object[]> data, final Integer sortByColumn) throws ConfigurationException, FormatterException {
+ printHeader(formatter, header, sql, parameters);
+
+ if (sortByColumn != null) {
+ Collections.sort(data, new Comparator<Object[]>() {
+
+ @Override
+ public int compare(Object[] o1, Object[] o2) {
+ String s1 = String.valueOf(o1[sortByColumn]);
+ String s2 = String.valueOf(o2[sortByColumn]);
+ return s1.compareTo(s2);
+ }
+ });
+ }
+
+ for (Object[] row : data) {
+ printRow(formatter, row);
+ }
+
+ printFooter(formatter);
+ }
+
+ private void printHeader(Formatter formatter, ColumnsHeader header, String sql, List<Parameter> parameters) {
+ formatter.writeStartStatement();
+ if (sql != null) {
+ formatter.writeQuery(sql);
+ if (parameters != null) {
+ formatter.writeParameters(parameters);
+ }
+ }
+ formatter.writeStartResultSet(header);
+ }
+
+ private void printRow(Formatter formatter, Object[] row) {
+ formatter.writeStartRow();
+ for (Object cell : row) {
+ formatter.writeColumnValue(cell);
+ }
+ formatter.writeEndRow();
+ }
+
+ private void printFooter(Formatter formatter) {
+ formatter.writeEndResultSet();
+ formatter.writeEndStatement();
+ }
+
+ private Formatter getFormatter() throws ConfigurationException, FormatterException {
+ String formatterName = options.getFormatterName();
+ formatterName = formatterName == null ? Configuration.DEFAULT_FORMATTER_PREFETCHING : formatterName;
+ FormatterDefinition fd = configurationProvider.getConfiguration().getFormatter(formatterName);
+ FormatterContext context = new FormatterContext(out, options.getFormatterProperties());
+ return fd.getInstance(context);
+ }
+
+ private ColumnsHeader constructHeader(HeaderField... fields) throws FormatterException {
+ try {
+ RowSetMetaDataImpl metaData = new RowSetMetaDataImpl();
+ metaData.setColumnCount(fields.length);
+
+ for (int i = 0; i < fields.length; i++) {
+ HeaderField hf = fields[i];
+ int sqlIndex = i + 1;
+ metaData.setColumnName(sqlIndex, hf.name);
+ metaData.setColumnLabel(sqlIndex, hf.name);
+ metaData.setColumnType(sqlIndex, hf.type.getCode());
+ metaData.setColumnTypeName(sqlIndex, hf.type.name());
+ }
+
+ return new ColumnsHeader(metaData);
+ } catch (SQLException e) {
+ throw new FormatterException("Error while constructing table headers", e);
+ }
+ }
+
+ private static class HeaderField {
+
+ String name;
+ SQLType type;
+
+ public HeaderField(String name, SQLType type) {
+ this.name = name;
+ this.type = type;
+ }
+ }
+
+ public enum InfoType {
+
+ HELP {
+ @Override
+ public void showInfo(InfoLister infoLister) {
+ infoLister.printResource(Constants.HELP_FILE);
+ }
+ },
+ VERSION {
+ @Override
+ public void showInfo(InfoLister infoLister) {
+ infoLister.printResource(Constants.VERSION_FILE);
+ }
+ },
+ LICENSE {
+ @Override
+ public void showInfo(InfoLister infoLister) {
+ infoLister.printResource(Constants.LICENSE_FILE);
+ }
+ },
+ JAVA_PROPERTIES {
+ @Override
+ public void showInfo(InfoLister infoLister) throws FormatterException, ConfigurationException {
+ infoLister.listJavaProperties();
+ }
+ },
+ ENVIRONMENT_VARIABLES {
+ @Override
+ public void showInfo(InfoLister infoLister) throws FormatterException, ConfigurationException {
+ infoLister.listEnvironmentVariables();
+ }
+ },
+ FORMATTERS {
+ @Override
+ public void showInfo(InfoLister infoLister) throws FormatterException, ConfigurationException {
+ infoLister.listFormatters();
+ }
+ },
+ FORMATTER_PROPERTIES {
+ @Override
+ public void showInfo(InfoLister infoLister) throws FormatterException, ConfigurationException {
+ infoLister.listFormatterProperties();
+ }
+ },
+ TYPES {
+ @Override
+ public void showInfo(InfoLister infoLister) throws FormatterException, ConfigurationException {
+ infoLister.listTypes();
+ }
+ },
+ JDBC_DRIVERS {
+ @Override
+ public void showInfo(InfoLister infoLister) throws ConfigurationException, FormatterException {
+ infoLister.listJdbcDrivers();
+ }
+ },
+ JDBC_PROPERTIES {
+ @Override
+ public void showInfo(InfoLister infoLister) throws ConfigurationException, FormatterException {
+ infoLister.listJdbcProperties();
+ }
+ },
+ DATABASES {
+ @Override
+ public void showInfo(InfoLister infoLister) throws FormatterException, ConfigurationException {
+ infoLister.listDatabases();
+ }
+ },
+ CONNECTION {
+ @Override
+ public void showInfo(InfoLister infoLister) throws FormatterException, ConfigurationException {
+ infoLister.testConnections();
+ }
+ };
+
+ public abstract void showInfo(InfoLister infoLister) throws ConfigurationException, FormatterException;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/InvalidOptionsException.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,66 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class InvalidOptionsException extends Exception {
+
+ private final Collection<OptionProblem> problems = new ArrayList<>();
+
+ public Collection<OptionProblem> getProblems() {
+ return Collections.unmodifiableCollection(problems);
+ }
+
+ public void addProblem(OptionProblem p) {
+ problems.add(p);
+ }
+
+ public boolean hasProblems() {
+ return !problems.isEmpty();
+ }
+
+ public static class OptionProblem {
+
+ private String description;
+ private Throwable exception;
+
+ public OptionProblem(String description) {
+ this.description = description;
+ }
+
+ public OptionProblem(String description, Throwable exception) {
+ this.description = description;
+ this.exception = exception;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public Throwable getException() {
+ return exception;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/NamedParameter.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,48 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk;
+
+import info.globalcode.sql.dk.configuration.NameIdentified;
+
+/**
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class NamedParameter extends Parameter implements NameIdentified {
+
+ private String name;
+
+ public NamedParameter(String name, Object value, SQLType type) {
+ super(value, type);
+ this.name = name;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return "NamedParameter {" + name + " = " + getValue() + "; " + getType() + "}";
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/Parameter.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,69 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk;
+
+import java.sql.Types;
+
+/**
+ * Parameter for {@linkplain SQLCommand}
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class Parameter {
+
+ /**
+ * @see Types
+ */
+ public static final SQLType DEFAULT_TYPE = SQLType.VARCHAR;
+ private Object value;
+ private SQLType type;
+
+ public Parameter() {
+ }
+
+ public Parameter(Object value, SQLType type) {
+ this.value = value;
+ if (type == null) {
+ this.type = DEFAULT_TYPE;
+ } else {
+ this.type = type;
+ }
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ public void setValue(Object value) {
+ this.value = value;
+ }
+
+ /**
+ * @see java.sql.Types
+ */
+ public SQLType getType() {
+ return type;
+ }
+
+ /**
+ * @see java.sql.Types
+ */
+ public void setType(SQLType type) {
+ this.type = type;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/SQLCommand.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,49 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.List;
+
+/**
+ * Represents SQL string and its parameters (if there are any).
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public abstract class SQLCommand {
+
+ private String query;
+
+ public SQLCommand(String query) {
+ this.query = query;
+ }
+
+ public PreparedStatement prepareStatement(Connection c) throws SQLException {
+ return c.prepareStatement(query);
+ }
+
+ public abstract void parametrize(PreparedStatement ps) throws SQLException;
+
+ public abstract List<? extends Parameter> getParameters();
+
+ public String getQuery() {
+ return query;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/SQLCommandNamed.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,155 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk;
+
+import static info.globalcode.sql.dk.Functions.findByName;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+/**
+ * Has named parameters.
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class SQLCommandNamed extends SQLCommand {
+
+ private static final Logger log = Logger.getLogger(SQLCommandNamed.class.getName());
+ private String namePrefix;
+ private String nameSuffix;
+ private List<NamedParameter> parameters;
+ private List<NamedParameter> parametersUsed = new ArrayList<>();
+ private StringBuilder updatedQuery;
+ private Pattern pattern;
+ private SQLCommandNumbered numbered;
+
+ public SQLCommandNamed(String query, List<NamedParameter> parameters, String namePrefix, String nameSuffix) {
+ super(query);
+ this.updatedQuery = new StringBuilder(query.length());
+ this.parameters = parameters;
+ this.namePrefix = namePrefix;
+ this.nameSuffix = nameSuffix;
+ }
+
+ @Override
+ public PreparedStatement prepareStatement(Connection c) throws SQLException {
+ return getSQLCommandNumbered().prepareStatement(c);
+ }
+
+ @Override
+ public void parametrize(PreparedStatement ps) throws SQLException {
+ getSQLCommandNumbered().parametrize(ps);
+ }
+
+ private void prepare() throws SQLException {
+ try {
+ buildPattern();
+ placeParametersAndUpdateQuery();
+ logPossiblyMissingParameters();
+ } catch (PatternSyntaxException e) {
+ throw new SQLException("Name prefix „" + namePrefix + "“ or suffix „" + nameSuffix + "“ contain a wrong regular expression. " + e.getLocalizedMessage(), e);
+ }
+ }
+
+ /**
+ * @return SQL command with named parameters converted to SQL command with numbered parameters
+ */
+ public SQLCommandNumbered getSQLCommandNumbered() throws SQLException {
+ if (numbered == null) {
+ prepare();
+ numbered = new SQLCommandNumbered(updatedQuery.toString(), parametersUsed);
+ }
+
+ return numbered;
+ }
+
+ /**
+ * Builds a regexp pattern that matches all parameter names (with prefix/suffix) and which has
+ * one group: parameter name (without prefix/suffix)
+ */
+ private void buildPattern() throws PatternSyntaxException {
+ StringBuilder patternString = new StringBuilder();
+
+ patternString.append(namePrefix);
+ patternString.append("(?<paramName>");
+ for (int i = 0; i < parameters.size(); i++) {
+ patternString.append(Pattern.quote(parameters.get(i).getName()));
+ if (i < parameters.size() - 1) {
+ patternString.append("|");
+ }
+ }
+ patternString.append(")");
+ patternString.append(nameSuffix);
+
+ pattern = Pattern.compile(patternString.toString());
+ }
+
+ private void placeParametersAndUpdateQuery() {
+ final String originalQuery = getQuery();
+ Matcher m = pattern.matcher(originalQuery);
+
+ int lastPosition = 0;
+ while (m.find(lastPosition)) {
+ String name = m.group("paramName");
+
+ updatedQuery.append(originalQuery.substring(lastPosition, m.start()));
+ updatedQuery.append("?");
+
+ parametersUsed.add(findByName(parameters, name));
+
+ lastPosition = m.end();
+ }
+ updatedQuery.append(originalQuery.substring(lastPosition, originalQuery.length()));
+
+ for (NamedParameter definedParameter : parameters) {
+ if (findByName(parametersUsed, definedParameter.getName()) == null) {
+ /**
+ * User can have predefined set of parameters and use them with different SQL
+ * queries that use only subset of these parameters → just warning, not exception.
+ */
+ log.log(Level.WARNING, "Parameter „{0}“ is defined but not used in the query: „{1}“", new Object[]{definedParameter.getName(), originalQuery});
+ }
+ }
+ }
+
+ private void logPossiblyMissingParameters() {
+ Pattern p = Pattern.compile(namePrefix + "(?<paramName>.+?)" + nameSuffix);
+ Matcher m = p.matcher(updatedQuery);
+ int lastPosition = 0;
+ while (m.find(lastPosition)) {
+ /**
+ * We have not parsed and understood the SQL query; the parameter-like looking string
+ * could be inside a literal part of the query → just warning, not exception.
+ */
+ log.log(Level.WARNING, "Possibly missing parameter „{0}“ in the query: „{1}“", new Object[]{m.group("paramName"), getQuery()});
+ lastPosition = m.end();
+ }
+ }
+
+ @Override
+ public List<NamedParameter> getParameters() {
+ return parameters;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/SQLCommandNumbered.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,51 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk;
+
+import static info.globalcode.sql.dk.Functions.notNull;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.List;
+
+/**
+ * Has ordinal/numbered parameters.
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class SQLCommandNumbered extends SQLCommand {
+
+ private List<? extends Parameter> parameters;
+
+ public SQLCommandNumbered(String query, List<? extends Parameter> parameters) {
+ super(query);
+ this.parameters = parameters;
+ }
+
+ @Override
+ public void parametrize(PreparedStatement ps) throws SQLException {
+ int i = 1;
+ for (Parameter p : notNull(parameters)) {
+ ps.setObject(i++, p.getValue(), p.getType().getCode());
+ }
+ }
+
+ @Override
+ public List<? extends Parameter> getParameters() {
+ return parameters;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/SQLType.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,95 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk;
+
+import java.sql.Types;
+
+/**
+ * Data types of SQL parameters.
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public enum SQLType {
+
+ /**
+ * Names must be upper case – user input is also converted to upper case → case insensitive
+ */
+ BIT(Types.BIT),
+ TINYINT(Types.TINYINT),
+ SMALLINT(Types.SMALLINT),
+ INTEGER(Types.INTEGER),
+ BIGINT(Types.BIGINT),
+ FLOAT(Types.FLOAT),
+ REAL(Types.REAL),
+ DOUBLE(Types.DOUBLE),
+ NUMERIC(Types.NUMERIC),
+ DECIMAL(Types.DECIMAL),
+ CHAR(Types.CHAR),
+ VARCHAR(Types.VARCHAR),
+ LONGVARCHAR(Types.LONGVARCHAR),
+ DATE(Types.DATE),
+ TIME(Types.TIME),
+ TIMESTAMP(Types.TIMESTAMP),
+ BINARY(Types.BINARY),
+ VARBINARY(Types.VARBINARY),
+ LONGVARBINARY(Types.LONGVARBINARY),
+ NULL(Types.NULL),
+ OTHER(Types.OTHER),
+ JAVA_OBJECT(Types.JAVA_OBJECT),
+ DISTINCT(Types.DISTINCT),
+ STRUCT(Types.STRUCT),
+ ARRAY(Types.ARRAY),
+ BLOB(Types.BLOB),
+ CLOB(Types.CLOB),
+ REF(Types.REF),
+ DATALINK(Types.DATALINK),
+ BOOLEAN(Types.BOOLEAN),
+ ROWID(Types.ROWID),
+ NCHAR(Types.NCHAR),
+ NVARCHAR(Types.NVARCHAR),
+ LONGNVARCHAR(Types.LONGNVARCHAR),
+ NCLOB(Types.NCLOB),
+ SQLXML(Types.SQLXML);
+ /** value from java.sql.Types */
+ private int code;
+
+ private SQLType(int code) {
+ this.code = code;
+ }
+
+ /**
+ * @see java.sql.Types.Types
+ */
+ public int getCode() {
+ return code;
+ }
+
+ /**
+ * @param code see {@linkplain java.sql.Types.Types}
+ * @return found SQLType
+ * @throws IllegalArgumentException if no data type has given code
+ */
+ public static SQLType valueOf(int code) {
+ for (SQLType t : values()) {
+ if (t.code == code) {
+ return t;
+ }
+ }
+ throw new IllegalArgumentException("No data type has code: " + code);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/Xmlns.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,33 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk;
+
+/**
+ * XML namespaces
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class Xmlns {
+
+ public static final String CONFIGURATION = "https://sql-dk.globalcode.info/xmlns/configuration";
+ public static final String BATCH_RESULT = "https://sql-dk.globalcode.info/xmlns/batchResult";
+ public static final String XHTML = "http://www.w3.org/1999/xhtml";
+
+ private Xmlns() {
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/batch/Batch.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,32 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.batch;
+
+import info.globalcode.sql.dk.SQLCommand;
+
+/**
+ * Iterator which reads SQL commands from encoded (serialized) batch.
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public interface Batch {
+
+ public boolean hasNext() throws BatchException;
+
+ public SQLCommand next() throws BatchException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/batch/BatchConstants.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,35 @@
+/**
+ * SQL-DK
+ * Copyright © 2014 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.batch;
+
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+
+/**
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class BatchConstants {
+
+ public static final Charset CHARSET = StandardCharsets.UTF_8;
+ public static final byte VERSION = 0x01;
+ public static final byte[] BATCH_HEADER = {0x00, 0x53, 0x51, 0x4C, VERSION};
+
+ private BatchConstants() {
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/batch/BatchDecoder.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,108 @@
+/**
+ * SQL-DK
+ * Copyright © 2014 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.batch;
+
+import info.globalcode.sql.dk.Parameter;
+import info.globalcode.sql.dk.SQLCommand;
+import info.globalcode.sql.dk.SQLCommandNumbered;
+import java.io.DataInputStream;
+import java.io.InputStream;
+import static info.globalcode.sql.dk.batch.BatchConstants.*;
+import static info.globalcode.sql.dk.Functions.toHex;
+import info.globalcode.sql.dk.SQLType;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class BatchDecoder {
+
+ public Batch decode(InputStream in) throws BatchException {
+ return new BatchFromStream(new DataInputStream(in));
+ }
+
+ private class BatchFromStream implements Batch {
+
+ private DataInputStream in;
+ private boolean hasNext;
+
+ public BatchFromStream(DataInputStream in) throws BatchException {
+ this.in = in;
+ hasNext = verifyHeader();
+ }
+
+ @Override
+ public boolean hasNext() throws BatchException {
+ return hasNext;
+ }
+
+ @Override
+ public SQLCommand next() throws BatchException {
+ try {
+ String sql = readNextString();
+
+ int paramCount = in.readInt();
+ List<Parameter> parameters = new ArrayList<>(paramCount);
+
+ for (int i = 0; i < paramCount; i++) {
+ SQLType type = SQLType.valueOf(in.readInt());
+ String value = readNextString();
+ parameters.add(new Parameter(value, type));
+ }
+
+ hasNext = verifyHeader();
+
+ SQLCommand sqlCommand = new SQLCommandNumbered(sql, parameters);
+ return sqlCommand;
+ } catch (IOException e) {
+ throw new BatchException("Unable to read batch", e);
+ }
+ }
+
+ private String readNextString() throws IOException {
+ byte[] buffer = new byte[in.readInt()];
+ in.read(buffer);
+ return new String(buffer, CHARSET);
+ }
+
+ /**
+ * @return true if correct batch header was found | false if EOF was found
+ * @throws BatchException if unexpected data was found (not batch header nor EOF)
+ */
+ private boolean verifyHeader() throws BatchException {
+ try {
+ byte[] buffer = new byte[BATCH_HEADER.length];
+ int bytesRead = in.read(buffer);
+
+ if (bytesRead == BATCH_HEADER.length && Arrays.equals(buffer, BATCH_HEADER)) {
+ return true;
+ } else if (bytesRead == -1) {
+ return false;
+ } else {
+ throw new BatchException("This is not SQL-DK batch: " + toHex(buffer));
+ }
+ } catch (IOException e) {
+ throw new BatchException("Unable to read batch header", e);
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/batch/BatchEncoder.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,83 @@
+/**
+ * SQL-DK
+ * Copyright © 2014 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.batch;
+
+import info.globalcode.sql.dk.Parameter;
+import info.globalcode.sql.dk.SQLCommand;
+import info.globalcode.sql.dk.SQLCommandNamed;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import static info.globalcode.sql.dk.batch.BatchConstants.*;
+import java.io.ByteArrayOutputStream;
+import java.sql.SQLException;
+import java.util.List;
+
+/**
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class BatchEncoder {
+
+ public int encode(SQLCommand sqlCommand, OutputStream out) throws BatchException {
+ try {
+ ByteArrayOutputStream bufferAOS = new ByteArrayOutputStream();
+ DataOutputStream buffer = new DataOutputStream(bufferAOS);
+
+ buffer.write(BATCH_HEADER);
+
+ if (sqlCommand instanceof SQLCommandNamed) {
+ sqlCommand = ((SQLCommandNamed) sqlCommand).getSQLCommandNumbered();
+ }
+
+ writeNextString(sqlCommand.getQuery(), buffer);
+
+ List<? extends Parameter> parameters = sqlCommand.getParameters();
+
+ buffer.writeInt(parameters.size());
+
+ for (Parameter p : parameters) {
+ buffer.writeInt(p.getType().getCode());
+ writeNextString((String) p.getValue(), buffer); // parameters are encoded before any preprocessing
+ }
+
+ buffer.flush();
+ bufferAOS.writeTo(out);
+ out.flush();
+ return bufferAOS.size();
+ } catch (IOException e) {
+ throw new BatchException("Unable to write SQL command: " + sqlCommand, e);
+ } catch (SQLException e) {
+ throw new BatchException("Unable to converd named SQL command to numbered: " + sqlCommand, e);
+ }
+ }
+
+ private void writeNextString(String s, DataOutputStream out) throws IOException {
+ byte[] bytes = toBytes(s);
+ out.writeInt(bytes.length);
+ out.write(bytes);
+ }
+
+ private static byte[] toBytes(String s) {
+ if (s == null) {
+ return new byte[]{};
+ } else {
+ return s.getBytes(CHARSET);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/batch/BatchException.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,42 @@
+/**
+ * SQL-DK
+ * Copyright © 2014 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.batch;
+
+import info.globalcode.sql.dk.DKException;
+
+/**
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class BatchException extends DKException {
+
+ public BatchException() {
+ }
+
+ public BatchException(String message) {
+ super(message);
+ }
+
+ public BatchException(Throwable cause) {
+ super(cause);
+ }
+
+ public BatchException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/configuration/CommandArgument.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,82 @@
+/**
+ * SQL-DK
+ * Copyright © 2015 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.configuration;
+
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlEnum;
+import javax.xml.bind.annotation.XmlEnumValue;
+import javax.xml.bind.annotation.XmlValue;
+
+/**
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class CommandArgument {
+
+ private String value;
+ private TYPE type;
+
+ @XmlEnum
+ public static enum TYPE {
+
+ /**
+ * value = literal (text) argument
+ */
+ @XmlEnumValue("literal")
+ LITERAL,
+ /**
+ * value will be substituted by hostname or IP address of the DB server
+ */
+ @XmlEnumValue("host")
+ HOST,
+ /**
+ * value will be substituted by the port of the DB server
+ */
+ @XmlEnumValue("port")
+ PORT,
+ /**
+ * value will be substituted by environmental variable of given name
+ */
+ @XmlEnumValue("env")
+ ENVIRONMENT_VARIABLE,
+ /**
+ * value will be substituted by database property of given name
+ */
+ @XmlEnumValue("dbProperty")
+ DB_PROPERTY;
+ }
+
+ @XmlValue
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ @XmlAttribute(name = "type")
+ public TYPE getType() {
+ return type;
+ }
+
+ public void setType(TYPE type) {
+ this.type = type;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/configuration/Configuration.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,173 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.configuration;
+
+import static info.globalcode.sql.dk.Xmlns.CONFIGURATION;
+import static info.globalcode.sql.dk.Functions.findByName;
+import info.globalcode.sql.dk.formatting.BarChartFormatter;
+import info.globalcode.sql.dk.formatting.SilentFormatter;
+import info.globalcode.sql.dk.formatting.SingleRecordFormatter;
+import info.globalcode.sql.dk.formatting.SingleValueFormatter;
+import info.globalcode.sql.dk.formatting.TabularFormatter;
+import info.globalcode.sql.dk.formatting.TabularPrefetchingFormatter;
+import info.globalcode.sql.dk.formatting.TabularWrappingFormatter;
+import info.globalcode.sql.dk.formatting.TeXFormatter;
+import info.globalcode.sql.dk.formatting.XhtmlFormatter;
+import info.globalcode.sql.dk.formatting.XmlFormatter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlTransient;
+
+/**
+ * Object representation of user configuration loaded from XML.
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+@XmlRootElement(name = "configuration", namespace = CONFIGURATION)
+public class Configuration {
+
+ private List<DatabaseDefinition> databases = new ArrayList<>();
+ private List<FormatterDefinition> formatters = new ArrayList<>();
+ /**
+ * is used if no formatter is specified on CLI nor in user configuration
+ */
+ public static final String DEFAULT_FORMATTER = TabularFormatter.NAME;
+ /**
+ * Can be used as default if prefetching is ok – for configuration listings (config is alread in
+ * memory, so this does not matter)
+ */
+ public static final String DEFAULT_FORMATTER_PREFETCHING = TabularPrefetchingFormatter.NAME;
+ private String defaultFormatter;
+ /**
+ * Default list of formatters. Is used if particular name is not found in user configuration.
+ */
+ private static final Collection<FormatterDefinition> buildInFormatters;
+
+ static {
+ Collection<FormatterDefinition> l = new ArrayList<>();
+ l.add(new FormatterDefinition(SilentFormatter.NAME, SilentFormatter.class.getName()));
+ l.add(new FormatterDefinition(SingleValueFormatter.NAME, SingleValueFormatter.class.getName()));
+ l.add(new FormatterDefinition(SingleRecordFormatter.NAME, SingleRecordFormatter.class.getName()));
+ l.add(new FormatterDefinition(XmlFormatter.NAME, XmlFormatter.class.getName()));
+ l.add(new FormatterDefinition(XhtmlFormatter.NAME, XhtmlFormatter.class.getName()));
+ l.add(new FormatterDefinition(TabularFormatter.NAME, TabularFormatter.class.getName()));
+ l.add(new FormatterDefinition(TabularPrefetchingFormatter.NAME, TabularPrefetchingFormatter.class.getName()));
+ l.add(new FormatterDefinition(TabularWrappingFormatter.NAME, TabularWrappingFormatter.class.getName()));
+ l.add(new FormatterDefinition(TeXFormatter.NAME, TeXFormatter.class.getName()));
+ //l.add(new FormatterDefinition(DsvFormatter.NAME, DsvFormatter.class.getName()));
+ //l.add(new FormatterDefinition(SystemCommandExecutor.NAME, SystemCommandExecutor.class.getName()));
+ l.add(new FormatterDefinition(BarChartFormatter.NAME, BarChartFormatter.class.getName()));
+ buildInFormatters = Collections.unmodifiableCollection(l);
+ }
+
+ @XmlElement(name = "database", namespace = CONFIGURATION)
+ public List<DatabaseDefinition> getDatabases() {
+ return databases;
+ }
+
+ public void setDatabases(List<DatabaseDefinition> databases) {
+ this.databases = databases;
+ }
+
+ /**
+ * @param name
+ * @return
+ * @throws ConfigurationException if no database with this name is configured
+ */
+ public DatabaseDefinition getDatabase(String name) throws ConfigurationException {
+ DatabaseDefinition dd = findByName(databases, name);
+ if (dd == null) {
+ throw new ConfigurationException("Database is not configured: " + name);
+ } else {
+ return dd;
+ }
+ }
+
+ /**
+ * @return only configured formatters
+ * @see #getBuildInFormatters()
+ * @see #getAllFormatters()
+ */
+ @XmlElement(name = "formatter", namespace = CONFIGURATION)
+ public List<FormatterDefinition> getFormatters() {
+ return formatters;
+ }
+
+ public void setFormatters(List<FormatterDefinition> formatters) {
+ this.formatters = formatters;
+ }
+
+ /**
+ * @param name name of desired formatter. Looking for this name in user configuration, then in
+ * buil-in formatters. If null, default from configuration or (if not configured) built-in
+ * default is used.
+ * @return formatter definition
+ * @throws ConfigurationException if no formatter with this name was found
+ */
+ public FormatterDefinition getFormatter(String name) throws ConfigurationException {
+ if (name == null) {
+ return defaultFormatter == null ? getFormatter(DEFAULT_FORMATTER) : getFormatter(defaultFormatter);
+ } else {
+ FormatterDefinition fd = findByName(formatters, name);
+ fd = fd == null ? findByName(buildInFormatters, name) : fd;
+ if (fd == null) {
+ throw new ConfigurationException("Formatter is not configured: " + name);
+ } else {
+ return fd;
+ }
+ }
+ }
+
+ /**
+ * @return only built-in formatters
+ * @see #getAllFormatters()
+ * @see #getFormatters()
+ */
+ @XmlTransient
+ public Collection<FormatterDefinition> getBuildInFormatters() {
+ return buildInFormatters;
+ }
+
+ /**
+ * @return built-in + configured formatters
+ * @see #getFormatters()
+ */
+ @XmlTransient
+ public Collection<FormatterDefinition> getAllFormatters() {
+ Collection<FormatterDefinition> allFormatters = new ArrayList<>();
+ allFormatters.addAll(buildInFormatters);
+ allFormatters.addAll(formatters);
+ return allFormatters;
+ }
+
+ /**
+ * @return name of default formatter, is used if name is not specified on CLI
+ */
+ @XmlElement(name = "defaultFormatter", namespace = CONFIGURATION)
+ public String getDefaultFormatter() {
+ return defaultFormatter;
+ }
+
+ public void setDefaultFormatter(String defaultFormatter) {
+ this.defaultFormatter = defaultFormatter;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/configuration/ConfigurationException.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,42 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.configuration;
+
+import info.globalcode.sql.dk.DKException;
+
+/**
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class ConfigurationException extends DKException {
+
+ public ConfigurationException() {
+ }
+
+ public ConfigurationException(String message) {
+ super(message);
+ }
+
+ public ConfigurationException(Throwable cause) {
+ super(cause);
+ }
+
+ public ConfigurationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/configuration/ConfigurationProvider.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,28 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.configuration;
+
+/**
+ * Use for lazy-loading of the configuration.
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public interface ConfigurationProvider {
+
+ public Configuration getConfiguration() throws ConfigurationException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/configuration/DatabaseDefinition.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,147 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.configuration;
+
+import static info.globalcode.sql.dk.Xmlns.CONFIGURATION;
+import info.globalcode.sql.dk.DatabaseConnection;
+import info.globalcode.sql.dk.jmx.ConnectionManagement;
+import java.sql.SQLException;
+import java.util.logging.Logger;
+import javax.xml.bind.annotation.XmlElement;
+
+/**
+ * Configured (but not yet connected) database connection.
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class DatabaseDefinition implements NameIdentified {
+
+ private static final Logger log = Logger.getLogger(DatabaseDefinition.class.getName());
+ /**
+ * database name in SQL-DK configuration
+ */
+ private String name;
+ /**
+ * JDBC URL
+ */
+ private String url;
+ /**
+ * JDBC user name
+ */
+ private String userName;
+ /**
+ * JDBC password
+ */
+ private String password;
+ /**
+ * optional JDBC driver – if empty, the DriverManager is used to lookup specific Driver for
+ * given URL
+ */
+ private String driver;
+ /**
+ * JDBC properties
+ */
+ private Properties properties = new Properties();
+ /**
+ * optional definition of tunnel to the remote database
+ */
+ private TunnelDefinition tunnel;
+
+ @XmlElement(name = "name", namespace = CONFIGURATION)
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @XmlElement(name = "url", namespace = CONFIGURATION)
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ @XmlElement(name = "userName", namespace = CONFIGURATION)
+ public String getUserName() {
+ return userName;
+ }
+
+ public void setUserName(String userName) {
+ this.userName = userName;
+ }
+
+ @XmlElement(name = "password", namespace = CONFIGURATION)
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public String getDriver() {
+ return driver;
+ }
+
+ public void setDriver(String driver) {
+ this.driver = driver;
+ }
+
+ @XmlElement(name = "property", namespace = CONFIGURATION)
+ public Properties getProperties() {
+ return properties;
+ }
+
+ public void setProperties(Properties properties) {
+ this.properties = properties;
+ }
+
+ public TunnelDefinition getTunnel() {
+ return tunnel;
+ }
+
+ public void setTunnel(TunnelDefinition tunnel) {
+ this.tunnel = tunnel;
+ }
+
+ /**
+ * @param properties ad-hoc properties from CLI options (for the JDBC driver)
+ * @param jmxBean JMX management bean for progress reporting | null = disable JMX
+ * @return
+ * @throws java.sql.SQLException
+ */
+ public DatabaseConnection connect(Properties properties, ConnectionManagement jmxBean) throws SQLException {
+ return new DatabaseConnection(this, properties, jmxBean);
+ }
+
+ /**
+ * @param properties
+ * @return
+ * @throws java.sql.SQLException
+ * @see #connect(info.globalcode.sql.dk.configuration.Properties, java.lang.String)
+ * With disabled JMX reporting.
+ */
+ public DatabaseConnection connect(Properties properties) throws SQLException {
+ return new DatabaseConnection(this, properties, null);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/configuration/FormatterDefinition.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,114 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.configuration;
+
+import static info.globalcode.sql.dk.Xmlns.CONFIGURATION;
+import info.globalcode.sql.dk.formatting.Formatter;
+import info.globalcode.sql.dk.formatting.FormatterContext;
+import info.globalcode.sql.dk.formatting.FormatterException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import javax.xml.bind.annotation.XmlElement;
+
+/**
+ * Configured (but not yet instantiated) formatter.
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class FormatterDefinition implements NameIdentified {
+
+ private String name;
+ private String className;
+ private Properties properties = new Properties();
+
+ public FormatterDefinition() {
+ }
+
+ public FormatterDefinition(String name, String className) {
+ this.name = name;
+ this.className = className;
+ }
+
+ public FormatterDefinition(String name, String className, Properties properties) {
+ this(name, className);
+ this.properties = properties;
+ }
+
+ @XmlElement(name = "name", namespace = CONFIGURATION)
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Filter's class. Must implement the
+ * <code>info.globalcode.sql.dk.formatting.Formatter</code> interface.
+ * Subclassing the
+ * <code>info.globalcode.sql.dk.formatting.AbstractFormatter</code> is strongly recommended.
+ * The constructor must accept one parameter:
+ * <code>info.globalcode.sql.dk.formatting.FormatterContext</code>
+ *
+ * @return fully qualified class name
+ */
+ @XmlElement(name = "class", namespace = CONFIGURATION)
+ public String getClassName() {
+ return className;
+ }
+
+ public void setClassName(String className) {
+ this.className = className;
+ }
+
+ @XmlElement(name = "property", namespace = CONFIGURATION)
+ public Properties getProperties() {
+ return properties;
+ }
+
+ public void setProperties(Properties properties) {
+ this.properties = properties;
+ }
+
+ /**
+ * @param context
+ * @return
+ * @throws FormatterException
+ */
+ public Formatter getInstance(FormatterContext context) throws FormatterException {
+ context.getProperties().setDefaults(properties);
+ try {
+ Constructor constructor = Class.forName(className).getConstructor(context.getClass());
+
+ Object instance = constructor.newInstance(context);
+ if (instance instanceof Formatter) {
+ return (Formatter) instance;
+ } else {
+ throw new FormatterException("Formatter " + instance + " does not implement the " + Formatter.class.getName() + " interface");
+ }
+ } catch (ClassNotFoundException e) {
+ throw new FormatterException("Formatter class does not exist: " + className, e);
+ } catch (NoSuchMethodException e) {
+ throw new FormatterException("Formatter class with no valid constructor: " + className, e);
+ } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
+ throw new FormatterException("Formatter's constructor caused an error: " + className, e);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/configuration/Loader.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,101 @@
+/**
+ * SQL-DK
+ * Copyright © 2015 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.configuration;
+
+import info.globalcode.sql.dk.*;
+import static info.globalcode.sql.dk.DatabaseConnection.JDBC_PROPERTY_USER;
+import static info.globalcode.sql.dk.DatabaseConnection.JDBC_PROPERTY_PASSWORD;
+import java.sql.Connection;
+import java.sql.Driver;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.Unmarshaller;
+
+/**
+ * Configuration loader – deserializes Configuration from the XML file.
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class Loader {
+
+ private static final Logger log = Logger.getLogger(Loader.class.getName());
+
+ public Configuration loadConfiguration() throws ConfigurationException {
+ try {
+ JAXBContext jaxb = JAXBContext.newInstance(Configuration.class.getPackage().getName(), Configuration.class.getClassLoader());
+ Unmarshaller u = jaxb.createUnmarshaller();
+ return (Configuration) u.unmarshal(Constants.CONFIG_FILE);
+ } catch (Exception e) {
+ throw new ConfigurationException("Unable to load configuration from " + Constants.CONFIG_FILE, e);
+ }
+ }
+
+ /**
+ * JDBC connection should not be used directly in SQL-DK.
+ *
+ * @see DatabaseDefinition#connect(info.globalcode.sql.dk.configuration.Properties)
+ * @param properties
+ * @param databaseDefinition
+ * @return
+ * @throws java.sql.SQLException
+ */
+ public static Connection jdbcConnect(DatabaseDefinition databaseDefinition, Properties properties) throws SQLException {
+ synchronized (properties) {
+ /**
+ * Avoid rewriting the properties. Usually, the connection is created only once, but
+ * with --test-connection and with SQL-DK JDBC driver, it might be reused.
+ */
+ properties = properties.clone();
+ }
+ if (properties.hasProperty(JDBC_PROPERTY_PASSWORD)) {
+ log.log(Level.WARNING, "Passing DB password as CLI parameter is insecure!");
+ }
+ Properties credentials = new Properties();
+ credentials.add(new Property(JDBC_PROPERTY_USER, databaseDefinition.getUserName()));
+ credentials.add(new Property(JDBC_PROPERTY_PASSWORD, databaseDefinition.getPassword()));
+ credentials.setDefaults(databaseDefinition.getProperties());
+ properties.setDefaults(credentials);
+ java.util.Properties javaProperties = properties.getJavaProperties();
+
+ String driverClassName = databaseDefinition.getDriver();
+ final String url = databaseDefinition.getUrl();
+ if (driverClassName == null) {
+ log.log(Level.FINE, "Using DriverManager to create connection for „{0}“", url);
+ return DriverManager.getConnection(url, javaProperties);
+ } else {
+ log.log(Level.FINE, "Using custom Driver „{0}“ to create connection for „{1}“", new Object[]{driverClassName, url});
+ try {
+ Class<Driver> driverClass = (Class<Driver>) Class.forName(driverClassName);
+ Driver driver = driverClass.newInstance();
+ Connection connection = driver.connect(url, javaProperties);
+ if (connection == null) {
+ log.log(Level.SEVERE, "Driver „{0}“ returend null → it does not accept the URL: „{1}“", new Object[]{driverClassName, url});
+ throw new SQLException("Unable to connect: driver returned null.");
+ } else {
+ return connection;
+ }
+ } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | SQLException e) {
+ throw new SQLException("Unable to connect usig specific driver: " + driverClassName, e);
+ }
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/configuration/NameIdentified.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,27 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.configuration;
+
+/**
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public interface NameIdentified {
+
+ public String getName();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/configuration/Properties.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,129 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.configuration;
+
+import java.util.ArrayList;
+import javax.xml.bind.annotation.XmlTransient;
+import static info.globalcode.sql.dk.Functions.findByName;
+import java.util.Collections;
+
+/**
+ * <p>
+ * List of configurables.</p>
+ *
+ * <p>
+ * Can be backed by defaults – if value for given name is nof found in this instance, we will
+ * look into defaults. Methods also accept defaultValue parameter – is used if property is nof found
+ * even in default properties.</p>
+ *
+ * <p>
+ * Typical use: </p>
+ * <ul>
+ * <li>this instance – ad-hoc properties from CLI options</li>
+ * <li>default properties – from config file</li>
+ * <li>defaultValue – hardcoded default</li>
+ * </ul>
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class Properties extends ArrayList<Property> implements Cloneable {
+
+ private Properties defaults;
+
+ public Properties() {
+ }
+
+ public Properties(int initialCapacity) {
+ super(initialCapacity);
+ }
+
+ @XmlTransient
+ public Properties getDefaults() {
+ return defaults;
+ }
+
+ public void setDefaults(Properties defaults) {
+ this.defaults = defaults;
+ }
+
+ /**
+ * @param defaults the last/deepest defaults
+ */
+ public void setLastDefaults(Properties defaults) {
+ if (this.defaults == null) {
+ this.defaults = defaults;
+ } else {
+ this.defaults.setLastDefaults(defaults);
+ }
+ }
+
+ private Property findProperty(String name) {
+ Property p = findByName(this, name);
+ if (p == null && defaults != null) {
+ p = defaults.findProperty(name);
+ }
+ return p;
+ }
+
+ public String getString(String name, String defaultValue) {
+ Property p = findProperty(name);
+ return p == null ? defaultValue : p.getValue();
+ }
+
+ public boolean getBoolean(String name, boolean defaultValue) {
+ Property p = findProperty(name);
+ return p == null ? defaultValue : Boolean.valueOf(p.getValue());
+ }
+
+ public int getInteger(String name, int defaultValue) {
+ Property p = findProperty(name);
+ return p == null ? defaultValue : Integer.valueOf(p.getValue());
+ }
+
+ public boolean hasProperty(String name) {
+ return findByName(this, name) != null;
+ }
+
+ @Override
+ public Properties clone() {
+ Properties clone = new Properties(size());
+ Collections.copy(clone, this);
+ return clone;
+ }
+
+ /**
+ * @return merged this and backing defaults as Java Properties
+ */
+ public java.util.Properties getJavaProperties() {
+ java.util.Properties javaProperties = new java.util.Properties();
+ duplicateTo(javaProperties);
+ return javaProperties;
+ }
+
+ private void duplicateTo(java.util.Properties javaProperties) {
+ if (defaults != null) {
+ defaults.duplicateTo(javaProperties);
+ }
+ for (Property p : this) {
+ String value = p.getValue();
+ if (value != null) {
+ javaProperties.setProperty(p.getName(), value);
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/configuration/Property.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,69 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.configuration;
+
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlValue;
+
+/**
+ * One configurable
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class Property implements NameIdentified, Cloneable {
+
+ private String name;
+ private String value;
+
+ public Property() {
+ }
+
+ public Property(String name, String value) {
+ this.name = name;
+ this.value = value;
+ }
+
+ @XmlAttribute(name = "name")
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @XmlValue
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ @Override
+ public String toString() {
+ return name + "='" + value + "'";
+ }
+
+ @Override
+ protected Property clone() {
+ return new Property(name, value);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/configuration/PropertyDeclaration.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,57 @@
+/**
+ * SQL-DK
+ * Copyright © 2015 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.configuration;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import java.lang.annotation.Target;
+
+/**
+ * Declaration of the (formatter) properties – for documentation purposes.
+ *
+ * TODO: automatically inject properties (configured, ad-hoc, default ones) to the formatters
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+@Retention(RUNTIME)
+@Target({ElementType.TYPE})
+@Repeatable(PropertyDeclarations.class)
+public @interface PropertyDeclaration {
+
+ /**
+ * @return name of the property
+ */
+ String name();
+
+ /**
+ * @return data type of the value: String, numbers, Boolean or Enum
+ */
+ Class type();
+
+ /**
+ * @return documentation for the users
+ */
+ String description();
+
+ /**
+ * @return default value of this property
+ */
+ String defaultValue();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/configuration/PropertyDeclarations.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,35 @@
+/**
+ * SQL-DK
+ * Copyright © 2015 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.configuration;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import java.lang.annotation.Target;
+
+/**
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+@Retention(RUNTIME)
+@Target({ElementType.TYPE})
+public @interface PropertyDeclarations {
+
+ PropertyDeclaration[] value();
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/configuration/TunnelDefinition.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,51 @@
+/**
+ * SQL-DK
+ * Copyright © 2015 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.configuration;
+
+import static info.globalcode.sql.dk.Xmlns.CONFIGURATION;
+import java.util.List;
+import javax.xml.bind.annotation.XmlElement;
+
+/**
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class TunnelDefinition {
+
+ private String command;
+ private List<CommandArgument> arguments;
+
+ @XmlElement(name = "command", namespace = CONFIGURATION)
+ public String getCommand() {
+ return command;
+ }
+
+ public void setCommand(String command) {
+ this.command = command;
+ }
+
+ @XmlElement(name = "argument", namespace = CONFIGURATION)
+ public List<CommandArgument> getArguments() {
+ return arguments;
+ }
+
+ public void setArguments(List<CommandArgument> arguments) {
+ this.arguments = arguments;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/formatting/AbstractFormatter.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,254 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.formatting;
+
+import info.globalcode.sql.dk.Parameter;
+import info.globalcode.sql.dk.configuration.DatabaseDefinition;
+import java.util.EmptyStackException;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Stack;
+
+/**
+ * <ol>
+ * <li>ensures integrity – if methods are called in correct order and context</li>
+ * <li>provides default implmentations of methods that does not produce any output for given
+ * events</li>
+ * </ol>
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public abstract class AbstractFormatter implements Formatter {
+
+ private Stack<State> state = new Stack<>();
+ private FormatterContext formatterContext;
+ private ColumnsHeader currentColumnsHeader;
+ private String currentQuery;
+ private int currentColumnsCount;
+ private int currentRowCount;
+
+ public AbstractFormatter(FormatterContext formatterContext) {
+ this.formatterContext = formatterContext;
+ state.push(State.ROOT);
+ }
+
+ /*
+ * root
+ * .batch
+ * ..database
+ * ...statement
+ * ....@query
+ * ....@parameters
+ * ....resultSet
+ * .....row
+ * ......@columnValue
+ * ....@updatesResult
+ */
+ protected enum State {
+
+ ROOT,
+ BATCH,
+ DATABASE,
+ STATEMENT,
+ RESULT_SET,
+ ROW
+ }
+
+ /**
+ * Go down in hierarchy.
+ * Pushes new state and verifies the old one.
+ *
+ * @param current the new state – currently entering
+ * @param expected expected previous states (any of them is valid)
+ * @return previous state
+ * @throws IllegalStateException if previous state was not one from expected
+ */
+ private State pushState(State current, EnumSet expected) {
+ State previous = state.peek();
+
+ if (expected.contains(previous)) {
+ state.push(current);
+ return previous;
+ } else {
+ throw new IllegalStateException("Formatter was in wrong state: " + previous + " when it should be in one of: " + expected);
+ }
+ }
+
+ protected State peekState(EnumSet expected) {
+ State current = state.peek();
+
+ if (expected.contains(current)) {
+ return current;
+ } else {
+ throw new IllegalStateException("Formatter is in wrong state: " + current + " when it should be in one of: " + expected);
+ }
+
+ }
+
+ /**
+ * Go up in hierarchy.
+ * Pops the superior state/branch.
+ *
+ * @param expected expected superior state
+ * @return the superior state
+ * @throws IllegalStateException if superior state was not one from expected or if there is no
+ * more superior state (we are at root level)
+ */
+ private State popState(EnumSet expected) {
+ try {
+ state.pop();
+ State superior = state.peek();
+ if (expected.contains(superior)) {
+ return superior;
+ } else {
+ throw new IllegalStateException("Formatter had wrong superior state: " + superior + " when it should be in one of: " + expected);
+ }
+ } catch (EmptyStackException e) {
+ throw new IllegalStateException("Formatter was already at root level – there is nothing above that.", e);
+ }
+ }
+
+ @Override
+ public void writeStartBatch() {
+ pushState(State.BATCH, EnumSet.of(State.ROOT));
+ }
+
+ @Override
+ public void writeEndBatch() {
+ popState(EnumSet.of(State.ROOT));
+ }
+
+ @Override
+ public void writeStartDatabase(DatabaseDefinition databaseDefinition) {
+ pushState(State.DATABASE, EnumSet.of(State.BATCH));
+ }
+
+ @Override
+ public void writeEndDatabase() {
+ popState(EnumSet.of(State.BATCH));
+ }
+
+ @Override
+ public void writeStartStatement() {
+ pushState(State.STATEMENT, EnumSet.of(State.DATABASE));
+ }
+
+ @Override
+ public void writeEndStatement() {
+ popState(EnumSet.of(State.DATABASE));
+ }
+
+ @Override
+ public void writeStartResultSet(ColumnsHeader header) {
+ pushState(State.RESULT_SET, EnumSet.of(State.STATEMENT));
+ currentRowCount = 0;
+ currentColumnsHeader = header;
+ }
+
+ @Override
+ public void writeEndResultSet() {
+ popState(EnumSet.of(State.STATEMENT));
+ currentColumnsHeader = null;
+ }
+
+ @Override
+ public void writeQuery(String sql) {
+ peekState(EnumSet.of(State.STATEMENT));
+
+ if (currentColumnsHeader == null) {
+ currentQuery = sql;
+ } else {
+ throw new IllegalStateException("Query string '" + sql + "' must be set before columns header – was already set: " + currentColumnsHeader);
+ }
+ }
+
+ @Override
+ public void writeParameters(List<? extends Parameter> parameters) {
+ peekState(EnumSet.of(State.STATEMENT));
+
+ if (currentColumnsHeader != null) {
+ throw new IllegalStateException("Parameters '" + parameters + "' must be set before columns header – was already set: " + currentColumnsHeader);
+ }
+
+ if (currentQuery == null && parameters != null) {
+ throw new IllegalStateException("Parameters '" + parameters + "' must be set after query – was not yet set.");
+ }
+ }
+
+ @Override
+ public void writeStartRow() {
+ pushState(State.ROW, EnumSet.of(State.RESULT_SET));
+ currentColumnsCount = 0;
+ currentRowCount++;
+ }
+
+ @Override
+ public void writeEndRow() {
+ popState(EnumSet.of(State.RESULT_SET));
+ }
+
+ @Override
+ public void writeColumnValue(Object value) {
+ peekState(EnumSet.of(State.ROW));
+ currentColumnsCount++;
+
+ int declaredCount = currentColumnsHeader.getColumnCount();
+ if (currentColumnsCount > declaredCount) {
+ throw new IllegalStateException("Current columns count is " + currentColumnsCount + " which is more than declared " + declaredCount + " in header.");
+ }
+ }
+
+ @Override
+ public void writeUpdatesResult(int updatedRowsCount) {
+ peekState(EnumSet.of(State.STATEMENT));
+ }
+
+ @Override
+ public void close() throws FormatterException {
+ }
+
+ public FormatterContext getFormatterContext() {
+ return formatterContext;
+ }
+
+ protected ColumnsHeader getCurrentColumnsHeader() {
+ return currentColumnsHeader;
+ }
+
+ /**
+ * @return column number, 1 = first
+ */
+ protected int getCurrentColumnsCount() {
+ return currentColumnsCount;
+ }
+
+ protected boolean isCurrentColumnFirst() {
+ return currentColumnsCount == 1;
+ }
+
+ protected boolean isCurrentColumnLast() {
+ return currentColumnsCount == currentColumnsHeader.getColumnCount();
+ }
+
+ /**
+ * @return row number, 1 = first
+ */
+ protected int getCurrentRowCount() {
+ return currentRowCount;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/formatting/AbstractXmlFormatter.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,241 @@
+/**
+ * SQL-DK
+ * Copyright © 2014 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.formatting;
+
+import info.globalcode.sql.dk.ColorfulPrintWriter;
+import info.globalcode.sql.dk.ColorfulPrintWriter.TerminalColor;
+import java.util.Stack;
+import javax.xml.namespace.QName;
+import static info.globalcode.sql.dk.Functions.isEmpty;
+import static info.globalcode.sql.dk.Functions.toHex;
+import info.globalcode.sql.dk.configuration.PropertyDeclaration;
+import static info.globalcode.sql.dk.formatting.CommonProperties.COLORFUL;
+import static info.globalcode.sql.dk.formatting.CommonProperties.COLORFUL_DESCRIPTION;
+import java.nio.charset.Charset;
+import java.util.EmptyStackException;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <p>
+ * Provides helper methods for printing pretty intended and optionally colorful (syntax highlighted)
+ * XML output.
+ * </p>
+ *
+ * <p>
+ * Must be used with care – bad usage can lead to invalid XML (e.g. using undeclared namespaces).
+ * </p>
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+@PropertyDeclaration(name = COLORFUL, defaultValue = "false", type = Boolean.class, description = COLORFUL_DESCRIPTION)
+@PropertyDeclaration(name = AbstractXmlFormatter.PROPERTY_INDENT, defaultValue = AbstractXmlFormatter.PROPERTY_INDENT_DEFAULT, type = String.class, description = "tab or sequence of spaces used for indentation of nested elements")
+@PropertyDeclaration(name = AbstractXmlFormatter.PROPERTY_INDENT_TEXT, defaultValue = "true", type = Boolean.class, description = "whether text with line breaks should be indented; if not original whitespace will be preserved.")
+public abstract class AbstractXmlFormatter extends AbstractFormatter {
+
+ private static final Logger log = Logger.getLogger(AbstractXmlFormatter.class.getName());
+ public static final String PROPERTY_INDENT = "indent";
+ protected static final String PROPERTY_INDENT_DEFAULT = "\t";
+ public static final String PROPERTY_INDENT_TEXT = "indentText";
+ private static final TerminalColor ELEMENT_COLOR = TerminalColor.Magenta;
+ private static final TerminalColor ATTRIBUTE_NAME_COLOR = TerminalColor.Green;
+ private static final TerminalColor ATTRIBUTE_VALUE_COLOR = TerminalColor.Yellow;
+ private static final TerminalColor XML_DECLARATION_COLOR = TerminalColor.Red;
+ private static final TerminalColor XML_DOCTYPE_COLOR = TerminalColor.Cyan;
+ private Stack<QName> treePosition = new Stack<>();
+ private final ColorfulPrintWriter out;
+ private final String indent;
+ private final boolean indentText;
+
+ public AbstractXmlFormatter(FormatterContext formatterContext) {
+ super(formatterContext);
+ boolean colorful = formatterContext.getProperties().getBoolean(COLORFUL, false);
+ out = new ColorfulPrintWriter(formatterContext.getOutputStream(), false, colorful);
+ indent = formatterContext.getProperties().getString(PROPERTY_INDENT, PROPERTY_INDENT_DEFAULT);
+ indentText = formatterContext.getProperties().getBoolean(PROPERTY_INDENT_TEXT, true);
+
+ if (!indent.matches("\\s*")) {
+ log.log(Level.WARNING, "Setting indent to „{0}“ is weird & freaky; in hex: {1}", new Object[]{indent, toHex(indent.getBytes())});
+ }
+
+ }
+
+ protected void printStartDocument() {
+ out.print(XML_DECLARATION_COLOR, "<?xml version=\"1.0\" encoding=\"" + Charset.defaultCharset().name() + "\"?>");
+ }
+
+ protected void printDoctype(String doctype) {
+ out.print(XML_DOCTYPE_COLOR, "\n<!DOCTYPE " + doctype + ">");
+ }
+
+ protected void printEndDocument() {
+ out.println();
+ out.flush();
+ if (!treePosition.empty()) {
+ throw new IllegalStateException("Some elements are not closed: " + treePosition);
+ }
+ }
+
+ protected void printStartElement(QName element) {
+ printStartElement(element, null);
+ }
+
+ protected Map<QName, String> singleAttribute(QName name, String value) {
+ Map<QName, String> attributes = new HashMap<>(2);
+ attributes.put(name, value);
+ return attributes;
+ }
+
+ protected void printStartElement(QName element, Map<QName, String> attributes) {
+ printStartElement(element, attributes, false);
+ }
+
+ /**
+ * @param empty whether element should be closed <codfe>… /></code> (has no content, do not
+ * call {@linkplain #printEndElement()})
+ */
+ private void printStartElement(QName element, Map<QName, String> attributes, boolean empty) {
+ printIndent();
+
+ out.print(ELEMENT_COLOR, "<" + toString(element));
+
+ if (attributes != null) {
+ for (Entry<QName, String> attribute : attributes.entrySet()) {
+ out.print(" ");
+ out.print(ATTRIBUTE_NAME_COLOR, toString(attribute.getKey()));
+ out.print("=");
+ out.print(ATTRIBUTE_VALUE_COLOR, '"' + escapeXmlAttribute(attribute.getValue()) + '"');
+ }
+ }
+
+ if (empty) {
+ out.print(ELEMENT_COLOR, "/>");
+ } else {
+ out.print(ELEMENT_COLOR, ">");
+ treePosition.add(element);
+ }
+
+ out.flush();
+ }
+
+ /**
+ * Prints text node wrapped in given element without indenting the text and adding line breaks
+ * (useful for short texts).
+ *
+ * @param attributes use {@linkplain LinkedHashMap} to preserve attributes order
+ */
+ protected void printTextElement(QName element, Map<QName, String> attributes, String text) {
+ printStartElement(element, attributes);
+
+ String[] lines = text.split("\\n");
+
+ if (indentText && lines.length > 1) {
+ for (String line : lines) {
+ printText(line, true);
+ }
+ printEndElement(true);
+ } else {
+ /*
+ * line breaks at the end of the text will be eaten – if you need them, use indentText = false
+ */
+ if (lines.length == 1 && text.endsWith("\n")) {
+ text = text.substring(0, text.length() - 1);
+ }
+
+ printText(text, false);
+ printEndElement(false);
+ }
+ }
+
+ protected void printEmptyElement(QName element, Map<QName, String> attributes) {
+ printStartElement(element, attributes, true);
+ }
+
+ protected void printEndElement() {
+ printEndElement(true);
+ }
+
+ private void printEndElement(boolean indent) {
+ try {
+ QName name = treePosition.pop();
+
+ if (indent) {
+ printIndent();
+ }
+
+ out.print(ELEMENT_COLOR, "</" + toString(name) + ">");
+ out.flush();
+
+ } catch (EmptyStackException e) {
+ throw new IllegalStateException("No more elements to end.", e);
+ }
+ }
+
+ protected void printText(String s, boolean indent) {
+ if (indent) {
+ printIndent();
+ }
+ out.print(escapeXmlText(s));
+ out.flush();
+ }
+
+ protected void printIndent() {
+ out.println();
+ for (int i = 0; i < treePosition.size(); i++) {
+ out.print(indent);
+ }
+ }
+
+ protected static QName qname(String name) {
+ return new QName(name);
+ }
+
+ protected static QName qname(String prefix, String name) {
+ return new QName(null, name, prefix);
+ }
+
+ private String toString(QName name) {
+ if (isEmpty(name.getPrefix(), true)) {
+ return escapeName(name.getLocalPart());
+ } else {
+ return escapeName(name.getPrefix()) + ":" + escapeName(name.getLocalPart());
+ }
+ }
+
+ private String escapeName(String s) {
+ // TODO: avoid ugly values in <name name="…"/>
+ return s;
+ }
+
+ private static String escapeXmlText(String s) {
+ return s.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
+ // Not needed:
+ // return s.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """).replaceAll("'", "'");
+ }
+
+ /**
+ * Expects attribute values enclosed in "quotes" not 'apostrophes'.
+ */
+ private static String escapeXmlAttribute(String s) {
+ return s.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/formatting/BarChartFormatter.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,104 @@
+/**
+ * SQL-DK
+ * Copyright © 2015 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.formatting;
+
+import info.globalcode.sql.dk.Functions;
+import info.globalcode.sql.dk.configuration.PropertyDeclaration;
+import info.globalcode.sql.dk.logging.LoggerProducer;
+import java.math.BigDecimal;
+import java.math.MathContext;
+import java.math.RoundingMode;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * TODO: min/max values – range for case that no value is 100 %
+ *
+ * TODO: multiple barcharts in same table (last column is still default) + multiple resultsets
+ *
+ * TODO: negative values - bar starting from the middle, not always from the left
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+@PropertyDeclaration(name = BarChartFormatter.PROPERTY_PRECISION, type = Integer.class, defaultValue = BarChartFormatter.PROPERTY_PRECISION_DEFAULT, description = "number of characters representing 100 % in the bar chart")
+public class BarChartFormatter extends TabularPrefetchingFormatter {
+
+ public static final String NAME = "barchart"; // bash-completion:formatter
+ public static final String PROPERTY_PRECISION = "precision";
+ protected static final String PROPERTY_PRECISION_DEFAULT = "100";
+ private static final MathContext mathContext = MathContext.DECIMAL128;
+ public static final Logger log = LoggerProducer.getLogger();
+ private final BigDecimal chartPrecision;
+ private final char chartFull;
+ private final char chartEmpty;
+
+ public BarChartFormatter(FormatterContext formatterContext) {
+ super(formatterContext);
+ chartPrecision = BigDecimal.valueOf(formatterContext.getProperties().getInteger(PROPERTY_PRECISION, Integer.parseInt(PROPERTY_PRECISION_DEFAULT)));
+ chartFull = isAsciiNostalgia() ? '#' : '█';
+ chartEmpty = isAsciiNostalgia() ? '~' : '░';
+ // TODO: consider using partial blocks for more precision: https://en.wikipedia.org/wiki/Block_Elements
+ }
+
+ @Override
+ protected void postprocessPrefetchedResultSet(ColumnsHeader currentHeader, List<Object[]> currentResultSet) {
+ super.postprocessPrefetchedResultSet(currentHeader, currentResultSet);
+
+ updateColumnWidth(currentHeader.getColumnCount(), chartPrecision.intValue());
+
+ BigDecimal maximum = BigDecimal.ZERO;
+ BigDecimal minimum = BigDecimal.ZERO;
+ int lastIndex = currentHeader.getColumnCount() - 1;
+
+ Object valueObject = null;
+ try {
+ for (Object[] row : currentResultSet) {
+ valueObject = row[lastIndex];
+ if (valueObject != null) {
+ BigDecimal value = new BigDecimal(valueObject.toString());
+ maximum = maximum.max(value);
+ minimum = minimum.min(value);
+ }
+ }
+
+ BigDecimal range = maximum.subtract(minimum);
+
+ for (Object[] row : currentResultSet) {
+ valueObject = row[lastIndex];
+ if (valueObject == null) {
+ row[lastIndex] = "";
+ } else {
+ BigDecimal value = new BigDecimal(valueObject.toString());
+ BigDecimal valueFromMinimum = value.subtract(minimum);
+
+ BigDecimal points = chartPrecision.divide(range, mathContext).multiply(valueFromMinimum, mathContext);
+ int pointsRounded = points.setScale(0, RoundingMode.HALF_UP).intValue();
+ row[lastIndex] = Functions.repeat(chartFull, pointsRounded) + Functions.repeat(chartEmpty, chartPrecision.intValue() - pointsRounded);
+ }
+ }
+
+ } catch (NumberFormatException e) {
+ // https://en.wiktionary.org/wiki/parsable
+ log.log(Level.SEVERE, "Last column must be number or an object with toString() value parsable to a number. But was „{0}“", valueObject);
+ // FIXME: throw FormatterException
+ throw e;
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/formatting/ColumnDescriptor.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,122 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.formatting;
+
+import java.sql.Types;
+
+/**
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class ColumnDescriptor {
+
+ private String name;
+ private String label;
+ private int type;
+ private String typeName;
+ private boolean firstColumn;
+ private boolean lastColumn;
+ private int columnNumber;
+
+ /**
+ * @return column name
+ * @see #getLabel()
+ */
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * @return label specified by the SQL AS clause
+ */
+ public String getLabel() {
+ return label;
+ }
+
+ public void setLabel(String label) {
+ this.label = label;
+ }
+
+ public int getType() {
+ return type;
+ }
+
+ public void setType(int type) {
+ this.type = type;
+ }
+
+ public String getTypeName() {
+ return typeName;
+ }
+
+ public void setTypeName(String typeName) {
+ this.typeName = typeName;
+ }
+
+ public boolean isFirstColumn() {
+ return firstColumn;
+ }
+
+ public void setFirstColumn(boolean firstColumn) {
+ this.firstColumn = firstColumn;
+ }
+
+ public boolean isLastColumn() {
+ return lastColumn;
+ }
+
+ public void setLastColumn(boolean lastColumn) {
+ this.lastColumn = lastColumn;
+ }
+
+ /**
+ * @return number of this column, 1 = first
+ */
+ public int getColumnNumber() {
+ return columnNumber;
+ }
+
+ public void setColumnNumber(int columnNumber) {
+ this.columnNumber = columnNumber;
+ }
+
+ public boolean isBoolean() {
+ return type == Types.BOOLEAN;
+ }
+
+ public boolean isNumeric() {
+ switch (type) {
+ case Types.BIGINT:
+ case Types.DECIMAL:
+ case Types.DOUBLE:
+ case Types.FLOAT:
+ case Types.INTEGER:
+ case Types.NUMERIC:
+ case Types.REAL:
+ case Types.SMALLINT:
+ case Types.TINYINT:
+ return true;
+ default:
+ return false;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/formatting/ColumnsHeader.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,70 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.formatting;
+
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class ColumnsHeader {
+
+ private ResultSetMetaData metaData;
+
+ public ColumnsHeader(ResultSetMetaData metaData) {
+ this.metaData = metaData;
+ }
+
+ public int getColumnCount() {
+ try {
+ return metaData.getColumnCount();
+ } catch (SQLException e) {
+ throw new IllegalStateException("Error during getting column count.", e);
+ }
+ }
+
+ public List<ColumnDescriptor> getColumnDescriptors() {
+ try {
+ int count = metaData.getColumnCount();
+ List<ColumnDescriptor> list = new ArrayList<>(count);
+
+ for (int i = 1; i <= count; i++) {
+ ColumnDescriptor cd = new ColumnDescriptor();
+
+ cd.setFirstColumn(i == 1);
+ cd.setLastColumn(i == count);
+ cd.setColumnNumber(i);
+
+ cd.setLabel(metaData.getColumnLabel(i));
+ cd.setName(metaData.getColumnName(i));
+ cd.setType(metaData.getColumnType(i));
+ cd.setTypeName(metaData.getColumnTypeName(i));
+ /** TODO: more properties */
+ list.add(cd);
+ }
+
+ return list;
+ } catch (SQLException e) {
+ throw new IllegalStateException("Error during building column descriptors.", e);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/formatting/CommonProperties.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,50 @@
+/**
+ * SQL-DK
+ * Copyright © 2015 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.formatting;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class CommonProperties {
+
+ private static final Map<Class, String> TYPE_SIMPLE_NAMES;
+
+ static {
+ Map<Class, String> m = new HashMap<>();
+ m.put(Boolean.class, "boolean");
+ m.put(String.class, "String");
+ m.put(Character.class, "char");
+ m.put(Integer.class, "int");
+ m.put(Long.class, "long");
+ m.put(Double.class, "double");
+ TYPE_SIMPLE_NAMES = Collections.unmodifiableMap(m);
+ }
+
+ public static String getSimpleTypeName(Class type) {
+ String name = TYPE_SIMPLE_NAMES.get(type);
+ return name == null ? type.getName() : name;
+ }
+
+ public static final String COLORFUL = "color";
+ public static final String COLORFUL_DESCRIPTION = "whether the output should be printed in color (ANSI Escape Sequences)";
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/formatting/FakeSqlArray.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,106 @@
+/**
+ * SQL-DK
+ * Copyright © 2014 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.formatting;
+
+import info.globalcode.sql.dk.SQLType;
+import java.sql.Array;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Map;
+
+/**
+ * Fake SQL array, for formatting purposes only
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class FakeSqlArray implements Array {
+
+ private static final UnsupportedOperationException exception = new UnsupportedOperationException("This is just a fake SQL array.");
+ private final Object[] data;
+ private final SQLType baseType;
+
+ public FakeSqlArray(Object[] data, SQLType baseType) {
+ this.data = data;
+ this.baseType = baseType;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder string = new StringBuilder();
+ for (Object o : data) {
+ string.append(o);
+ string.append("\n");
+ }
+ return string.toString();
+ }
+
+ @Override
+ public String getBaseTypeName() throws SQLException {
+ return baseType.name();
+ }
+
+ @Override
+ public int getBaseType() throws SQLException {
+ return baseType.getCode();
+ }
+
+ @Override
+ public Object getArray() throws SQLException {
+ return data;
+ }
+
+ @Override
+ public Object getArray(Map<String, Class<?>> map) throws SQLException {
+ throw exception;
+ }
+
+ @Override
+ public Object getArray(long index, int count) throws SQLException {
+ throw exception;
+ }
+
+ @Override
+ public Object getArray(long index, int count, Map<String, Class<?>> map) throws SQLException {
+ throw exception;
+ }
+
+ @Override
+ public ResultSet getResultSet() throws SQLException {
+ throw exception;
+ }
+
+ @Override
+ public ResultSet getResultSet(Map<String, Class<?>> map) throws SQLException {
+ throw exception;
+ }
+
+ @Override
+ public ResultSet getResultSet(long index, int count) throws SQLException {
+ throw exception;
+ }
+
+ @Override
+ public ResultSet getResultSet(long index, int count, Map<String, Class<?>> map) throws SQLException {
+ throw exception;
+ }
+
+ @Override
+ public void free() throws SQLException {
+ throw exception;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/formatting/Formatter.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,67 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.formatting;
+
+import info.globalcode.sql.dk.Parameter;
+import info.globalcode.sql.dk.configuration.DatabaseDefinition;
+import java.util.List;
+
+/**
+ * The formatter is responsible for printing the result sets and/or updates result (count of
+ * inserted/updated rows). The formatter can produce output in arbitrary format – text, some markup
+ * or even binary data.
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public interface Formatter extends AutoCloseable {
+
+ void writeStartBatch();
+
+ void writeStartDatabase(DatabaseDefinition databaseDefinition);
+
+ void writeEndDatabase();
+
+ void writeStartStatement();
+
+ void writeEndStatement();
+
+ void writeQuery(String sql);
+
+ void writeParameters(List<? extends Parameter> parameters);
+
+ void writeStartResultSet(ColumnsHeader header);
+
+ void writeEndResultSet();
+
+ void writeStartRow();
+
+ void writeColumnValue(Object value);
+
+ void writeEndRow();
+
+ void writeUpdatesResult(int updatedRowsCount);
+
+ void writeEndBatch();
+
+ /**
+ * If an error occurs (e.g. lost connection during result set reading) this method will be
+ * called even if there was no {@linkplain #writeEndBach()}.
+ */
+ @Override
+ void close() throws FormatterException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/formatting/FormatterContext.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,49 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.formatting;
+
+import info.globalcode.sql.dk.configuration.Properties;
+import java.io.OutputStream;
+
+/**
+ * To be passed from the SQL-DK core to the formatter.
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class FormatterContext {
+
+ private OutputStream outputStream;
+ private Properties properties;
+
+ public FormatterContext(OutputStream outputStream, Properties properties) {
+ this.outputStream = outputStream;
+ this.properties = properties;
+ }
+
+ public OutputStream getOutputStream() {
+ return outputStream;
+ }
+
+ public Properties getProperties() {
+ return properties;
+ }
+
+ public void setProperties(Properties properties) {
+ this.properties = properties;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/formatting/FormatterException.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,42 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.formatting;
+
+import info.globalcode.sql.dk.DKException;
+
+/**
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class FormatterException extends DKException {
+
+ public FormatterException() {
+ }
+
+ public FormatterException(String message) {
+ super(message);
+ }
+
+ public FormatterException(Throwable cause) {
+ super(cause);
+ }
+
+ public FormatterException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/formatting/SilentFormatter.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,33 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.formatting;
+
+/**
+ * Does not output anything, can be used instead of
+ * <code>/dev/null</code>.
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class SilentFormatter extends AbstractFormatter {
+
+ public static final String NAME = "silent"; // bash-completion:formatter
+
+ public SilentFormatter(FormatterContext formatterContext) {
+ super(formatterContext);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/formatting/SingleRecordFormatter.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,105 @@
+/**
+ * SQL-DK
+ * Copyright © 2015 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.formatting;
+
+import info.globalcode.sql.dk.ColorfulPrintWriter;
+import info.globalcode.sql.dk.Functions;
+import info.globalcode.sql.dk.configuration.PropertyDeclaration;
+import static info.globalcode.sql.dk.formatting.CommonProperties.COLORFUL;
+import static info.globalcode.sql.dk.formatting.CommonProperties.COLORFUL_DESCRIPTION;
+
+/**
+ * Formatter intended for printing one record (or few records) with many columns.
+ * Prints each colum name and its value on separate line.
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+@PropertyDeclaration(name = COLORFUL, defaultValue = "true", type = Boolean.class, description = COLORFUL_DESCRIPTION)
+public class SingleRecordFormatter extends AbstractFormatter {
+
+ public static final String NAME = "record"; // bash-completion:formatter
+ private final ColorfulPrintWriter out;
+ private boolean firstResult = true;
+
+ public SingleRecordFormatter(FormatterContext formatterContext) {
+ super(formatterContext);
+ out = new ColorfulPrintWriter(formatterContext.getOutputStream());
+ out.setColorful(formatterContext.getProperties().getBoolean(COLORFUL, true));
+ }
+
+ @Override
+ public void writeStartResultSet(ColumnsHeader header) {
+ super.writeStartResultSet(header);
+ printResultSeparator();
+ }
+
+ @Override
+ public void writeStartRow() {
+ super.writeStartRow();
+ printRecordSeparator();
+ out.print(ColorfulPrintWriter.TerminalColor.Red, "Record: ");
+ out.print(getCurrentRowCount());
+ println();
+ }
+
+ @Override
+ public void writeColumnValue(Object value) {
+ super.writeColumnValue(value);
+ String columnName = getCurrentColumnsHeader().getColumnDescriptors().get(getCurrentColumnsCount() - 1).getLabel();
+ out.print(ColorfulPrintWriter.TerminalColor.Green, columnName + ": ");
+ Functions.printValueWithWhitespaceReplaced(out, toString(value), null, ColorfulPrintWriter.TerminalColor.Red);
+ println();
+ }
+
+ private static String toString(Object value) {
+ return String.valueOf(value);
+ }
+
+ @Override
+ public void writeUpdatesResult(int updatedRowsCount) {
+ super.writeUpdatesResult(updatedRowsCount);
+ printResultSeparator();
+ out.print(ColorfulPrintWriter.TerminalColor.Red, "Updated records: ");
+ out.println(updatedRowsCount);
+ printBellAndFlush();
+ }
+
+ private void printBellAndFlush() {
+ out.bell();
+ out.flush();
+ }
+
+ private void println() {
+ out.println();
+ printBellAndFlush();
+ }
+
+ private void printRecordSeparator() {
+ if (getCurrentRowCount() > 1) {
+ println();
+ }
+ }
+
+ private void printResultSeparator() {
+ if (firstResult) {
+ firstResult = false;
+ } else {
+ println();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/formatting/SingleValueFormatter.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,52 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.formatting;
+
+import java.io.PrintWriter;
+
+/**
+ * Prints just the value without any formatting. If the result set contains multiple records or
+ * columns, the values are simply concatenate without any separators. If updates result is returned,
+ * the updated records count is printed.
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class SingleValueFormatter extends AbstractFormatter {
+
+ public static final String NAME = "single"; // bash-completion:formatter
+ private PrintWriter out;
+
+ public SingleValueFormatter(FormatterContext formatterContext) {
+ super(formatterContext);
+ this.out = new PrintWriter(formatterContext.getOutputStream());
+ }
+
+ @Override
+ public void writeColumnValue(Object value) {
+ super.writeColumnValue(value);
+ out.print(String.valueOf(value));
+ out.flush();
+ }
+
+ @Override
+ public void writeUpdatesResult(int updatedRowsCount) {
+ super.writeUpdatesResult(updatedRowsCount);
+ out.print(updatedRowsCount);
+ out.flush();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/formatting/TabularFormatter.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,308 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.formatting;
+
+import info.globalcode.sql.dk.ColorfulPrintWriter;
+import static info.globalcode.sql.dk.ColorfulPrintWriter.*;
+import info.globalcode.sql.dk.Functions;
+import static info.globalcode.sql.dk.Functions.lpad;
+import static info.globalcode.sql.dk.Functions.rpad;
+import static info.globalcode.sql.dk.Functions.repeat;
+import info.globalcode.sql.dk.configuration.PropertyDeclaration;
+import static info.globalcode.sql.dk.formatting.CommonProperties.COLORFUL;
+import static info.globalcode.sql.dk.formatting.CommonProperties.COLORFUL_DESCRIPTION;
+import java.sql.SQLException;
+import java.sql.SQLXML;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <p>
+ * Prints human-readable output – tables of result sets and text messages with update counts.
+ * </p>
+ *
+ * <p>
+ * Longer values might break the table – overflow the cells – see alternative tabular formatters and
+ * the {@linkplain #PROPERTY_TRIM} property.
+ * </p>
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ * @see TabularPrefetchingFormatter
+ * @see TabularWrappingFormatter
+ */
+@PropertyDeclaration(name = COLORFUL, defaultValue = "true", type = Boolean.class, description = COLORFUL_DESCRIPTION)
+@PropertyDeclaration(name = TabularFormatter.PROPERTY_ASCII, defaultValue = "false", type = Boolean.class, description = "whether to use ASCII table borders instead of unicode ones")
+@PropertyDeclaration(name = TabularFormatter.PROPERTY_TRIM, defaultValue = "false", type = Boolean.class, description = "whether to trim the values to fit the column width")
+@PropertyDeclaration(name = TabularFormatter.PROPERTY_HEADER_TYPE, defaultValue = "true", type = Boolean.class, description = "whether to print data types in column headers")
+public class TabularFormatter extends AbstractFormatter {
+
+ private static final Logger log = Logger.getLogger(TabularFormatter.class.getName());
+ public static final String NAME = "tabular"; // bash-completion:formatter
+ private static final String HEADER_TYPE_PREFIX = " (";
+ private static final String HEADER_TYPE_SUFFIX = ")";
+ public static final String PROPERTY_ASCII = "ascii";
+ public static final String PROPERTY_TRIM = "trim";
+ public static final String PROPERTY_HEADER_TYPE = "headerTypes";
+ protected ColorfulPrintWriter out;
+ private boolean firstResult = true;
+ private int[] columnWidth;
+ /**
+ * use ASCII borders instead of unicode ones
+ */
+ private final boolean asciiNostalgia;
+ /**
+ * Trim values if they are longer than cell size
+ */
+ private final boolean trimValues;
+ /**
+ * Print data type of each column in the header
+ */
+ private final boolean printHeaderTypes;
+
+ public TabularFormatter(FormatterContext formatterContext) {
+ super(formatterContext);
+ out = new ColorfulPrintWriter(formatterContext.getOutputStream());
+ asciiNostalgia = formatterContext.getProperties().getBoolean(PROPERTY_ASCII, false);
+ trimValues = formatterContext.getProperties().getBoolean(PROPERTY_TRIM, false);
+ printHeaderTypes = formatterContext.getProperties().getBoolean(PROPERTY_HEADER_TYPE, true);
+ out.setColorful(formatterContext.getProperties().getBoolean(COLORFUL, true));
+ }
+
+ @Override
+ public void writeStartResultSet(ColumnsHeader header) {
+ super.writeStartResultSet(header);
+ printResultSeparator();
+
+ initColumnWidths(header.getColumnCount());
+
+ printTableIndent();
+ printTableBorder("╭");
+
+ List<ColumnDescriptor> columnDescriptors = header.getColumnDescriptors();
+
+ for (ColumnDescriptor cd : columnDescriptors) {
+ // padding: make header cell at least same width as data cells in this column
+ int typeWidth = printHeaderTypes ? cd.getTypeName().length() + HEADER_TYPE_PREFIX.length() + HEADER_TYPE_SUFFIX.length() : 0;
+ cd.setLabel(rpad(cd.getLabel(), getColumnWidth(cd.getColumnNumber()) - typeWidth));
+ updateColumnWidth(cd.getColumnNumber(), cd.getLabel().length() + typeWidth);
+
+ if (!cd.isFirstColumn()) {
+ printTableBorder("┬");
+ }
+ printTableBorder(repeat('─', getColumnWidth(cd.getColumnNumber()) + 2));
+ }
+ printTableBorder("╮");
+ out.println();
+
+ for (ColumnDescriptor cd : columnDescriptors) {
+ if (cd.isFirstColumn()) {
+ printTableIndent();
+ printTableBorder("│ ");
+ } else {
+ printTableBorder(" │ ");
+ }
+ out.print(TerminalStyle.Bright, cd.getLabel());
+ if (printHeaderTypes) {
+ out.print(HEADER_TYPE_PREFIX);
+ out.print(cd.getTypeName());
+ out.print(HEADER_TYPE_SUFFIX);
+ }
+ if (cd.isLastColumn()) {
+ printTableBorder(" │");
+ }
+ }
+ out.println();
+
+ printTableIndent();
+ printTableBorder("├");
+ for (int i = 1; i <= header.getColumnCount(); i++) {
+ if (i > 1) {
+ printTableBorder("┼");
+ }
+ printTableBorder(repeat('─', getColumnWidth(i) + 2));
+ }
+ printTableBorder("┤");
+ out.println();
+
+ out.flush();
+ }
+
+ /**
+ * Must be called before {@linkplain #updateColumnWidth(int, int)} and
+ * {@linkplain #getColumnWidth(int)} for each result set.
+ *
+ * @param columnCount number of columns in current result set
+ */
+ protected void initColumnWidths(int columnCount) {
+ if (columnWidth == null) {
+ columnWidth = new int[columnCount];
+ }
+ }
+
+ protected void cleanColumnWidths() {
+ columnWidth = null;
+ }
+
+ @Override
+ public void writeColumnValue(Object value) {
+ super.writeColumnValue(value);
+ writeColumnValueInternal(value);
+ }
+
+ protected void writeColumnValueInternal(Object value) {
+
+ if (isCurrentColumnFirst()) {
+ printTableIndent();
+ printTableBorder("│ ");
+ } else {
+ printTableBorder(" │ ");
+ }
+
+ printValueWithWhitespaceReplaced(toString(value));
+
+ if (isCurrentColumnLast()) {
+ printTableBorder(" │");
+ }
+
+ }
+
+ protected void printValueWithWhitespaceReplaced(String text) {
+ Functions.printValueWithWhitespaceReplaced(out, text, TerminalColor.Cyan, TerminalColor.Red);
+ }
+
+ protected int getColumnWidth(int columnNumber) {
+ return columnWidth[columnNumber - 1];
+ }
+
+ private void setColumnWidth(int columnNumber, int width) {
+ columnWidth[columnNumber - 1] = width;
+ }
+
+ protected void updateColumnWidth(int columnNumber, int width) {
+ int oldWidth = getColumnWidth(columnNumber);
+ setColumnWidth(columnNumber, Math.max(width, oldWidth));
+
+ }
+
+ protected String toString(Object value) {
+ final int width = getColumnWidth(getCurrentColumnsCount());
+ String result;
+ if (value instanceof Number || value instanceof Boolean) {
+ result = lpad(String.valueOf(value), width);
+ } else {
+ if (value instanceof SQLXML) {
+ // TODO: move to a common method, share with other formatters
+ try {
+ value = ((SQLXML) value).getString();
+ } catch (SQLException e) {
+ log.log(Level.SEVERE, "Unable to format XML", e);
+ }
+ }
+
+ result = rpad(String.valueOf(value), width);
+ }
+ // ? value = (boolean) value ? "✔" : "✗";
+
+ if (trimValues && result.length() > width) {
+ result = result.substring(0, width - 1) + "…";
+ }
+
+ return result;
+ }
+
+ @Override
+ public void writeEndRow() {
+ super.writeEndRow();
+ writeEndRowInternal();
+ }
+
+ public void writeEndRowInternal() {
+ out.println();
+ out.flush();
+ }
+
+ @Override
+ public void writeEndResultSet() {
+ int columnCount = getCurrentColumnsHeader().getColumnCount();
+ super.writeEndResultSet();
+
+ printTableIndent();
+ printTableBorder("╰");
+ for (int i = 1; i <= columnCount; i++) {
+ if (i > 1) {
+ printTableBorder("┴");
+ }
+ printTableBorder(repeat('─', getColumnWidth(i) + 2));
+ }
+ printTableBorder("╯");
+ out.println();
+
+ cleanColumnWidths();
+
+ out.print(TerminalColor.Yellow, "Record count: ");
+ out.println(getCurrentRowCount());
+ out.bell();
+ out.flush();
+ }
+
+ @Override
+ public void writeUpdatesResult(int updatedRowsCount) {
+ super.writeUpdatesResult(updatedRowsCount);
+ printResultSeparator();
+ out.print(TerminalColor.Red, "Updated records: ");
+ out.println(updatedRowsCount);
+ out.bell();
+ out.flush();
+ }
+
+ @Override
+ public void writeEndDatabase() {
+ super.writeEndDatabase();
+ out.flush();
+ }
+
+ private void printResultSeparator() {
+ if (firstResult) {
+ firstResult = false;
+ } else {
+ out.println();
+ }
+ }
+
+ protected void printTableBorder(String border) {
+ if (asciiNostalgia) {
+ border = border.replaceAll("─", "-");
+ border = border.replaceAll("│", "|");
+ border = border.replaceAll("[╭┬╮├┼┤╰┴╯]", "+");
+ }
+
+ out.print(TerminalColor.Green, border);
+ }
+
+ protected void printTableIndent() {
+ out.print(" ");
+ }
+
+ /**
+ * @return whether should print only ASCII characters instead of unlimited Unicode.
+ */
+ protected boolean isAsciiNostalgia() {
+ return asciiNostalgia;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/formatting/TabularPrefetchingFormatter.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,119 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.formatting;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * <p>
+ * Prefetches whole result set and computes column widths. Whole table is flushed at once in
+ * {@linkplain #writeEndResultSet()}.
+ * </p>
+ *
+ * <p>
+ * Long values will not overflow the cells, but whole result set must be loaded into the memory.
+ * </p>
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class TabularPrefetchingFormatter extends TabularFormatter {
+
+ public static final String NAME = "tabular-prefetching"; // bash-completion:formatter
+ private ColumnsHeader currentHeader;
+ private List<Object[]> currentResultSet;
+ private Object[] currentRow;
+ private int currentColumnsCount;
+ private boolean prefetchDone = false;
+
+ public TabularPrefetchingFormatter(FormatterContext formatterContext) {
+ super(formatterContext);
+ }
+
+ @Override
+ protected int getCurrentColumnsCount() {
+ if (prefetchDone) {
+ return super.getCurrentColumnsCount();
+ } else {
+ return currentColumnsCount;
+ }
+ }
+
+ @Override
+ public void writeStartResultSet(ColumnsHeader header) {
+ currentResultSet = new ArrayList<>();
+ currentHeader = header;
+ initColumnWidths(header.getColumnCount());
+ }
+
+ @Override
+ public void writeStartRow() {
+ currentRow = new Object[currentHeader.getColumnCount()];
+ currentResultSet.add(currentRow);
+ currentColumnsCount = 0;
+ }
+
+ @Override
+ public void writeColumnValue(Object value) {
+ currentRow[currentColumnsCount] = value;
+ currentColumnsCount++;
+ String textRepresentation = toString(value);
+ /** TODO: count only printable characters (currently not an issue) */
+ updateColumnWidth(currentColumnsCount, textRepresentation.length());
+ }
+
+ @Override
+ public void writeEndRow() {
+ // do nothing
+ }
+
+ @Override
+ public void writeEndResultSet() {
+ prefetchDone = true;
+
+ postprocessPrefetchedResultSet(currentHeader, currentResultSet);
+
+ super.writeStartResultSet(currentHeader);
+
+ for (Object[] row : currentResultSet) {
+ super.writeStartRow();
+ for (Object cell : row) {
+ super.writeColumnValue(cell);
+ }
+ super.writeEndRow();
+ }
+
+ currentColumnsCount = 0;
+ currentHeader = null;
+ currentRow = null;
+ currentResultSet = null;
+ super.writeEndResultSet();
+ prefetchDone = false;
+ }
+
+ /**
+ * Optional post-processing – override in sub-classes if needed.
+ * Don't forget to {@linkplain #updateColumnWidth(int, int)}
+ *
+ * @param currentHeader
+ * @param currentResultSet
+ */
+ protected void postprocessPrefetchedResultSet(ColumnsHeader currentHeader, List<Object[]> currentResultSet) {
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/formatting/TabularWrappingFormatter.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,116 @@
+/**
+ * SQL-DK
+ * Copyright © 2014 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.formatting;
+
+import info.globalcode.sql.dk.ColorfulPrintWriter.TerminalColor;
+import java.util.ArrayList;
+import java.util.List;
+import static info.globalcode.sql.dk.Functions.lpad;
+import static info.globalcode.sql.dk.Functions.rpad;
+import static info.globalcode.sql.dk.Functions.repeat;
+
+/**
+ * Longer values are line-wrapped – the cell then contains multiple lines. Marks are added to
+ * signalize forced line ends (not present in original data).
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class TabularWrappingFormatter extends TabularFormatter {
+
+ public static final String NAME = "tabular-wrapping"; // bash-completion:formatter
+ private List<String[]> currentRow;
+
+ public TabularWrappingFormatter(FormatterContext formatterContext) {
+ super(formatterContext);
+ }
+
+ @Override
+ public void writeStartResultSet(ColumnsHeader header) {
+ super.writeStartResultSet(header);
+ currentRow = new ArrayList<>(header.getColumnCount());
+ }
+
+ @Override
+ protected void writeColumnValueInternal(Object value) {
+ boolean rightAlign = value instanceof Number || value instanceof Boolean;
+ String valueString = String.valueOf(value);
+ int columnWidth = getColumnWidth(getCurrentColumnsCount()) - 1; // -1 = space for new line symbol
+ currentRow.add(wrapLines(valueString, columnWidth, rightAlign));
+ }
+
+ @Override
+ public void writeEndRow() {
+ super.writeEndRow();
+
+ int wrappedLine = 0;
+ boolean hasMoreWrappedLines;
+
+ do {
+ hasMoreWrappedLines = false;
+ for (int i = 0; i < currentRow.size(); i++) {
+ if (i == 0) {
+ printTableIndent();
+ printTableBorder("│ ");
+ } else {
+ printTableBorder(" │ ");
+ }
+ String[] columnArray = currentRow.get(i);
+ if (wrappedLine < columnArray.length) {
+ printValueWithWhitespaceReplaced(columnArray[wrappedLine]);
+
+ if (wrappedLine < columnArray.length - 1) {
+ out.print(TerminalColor.Red, "↩");
+ hasMoreWrappedLines = true;
+ } else {
+ out.print(" ");
+ }
+
+ } else {
+ out.print(repeat(' ', getColumnWidth(i + 1)));
+ }
+
+ if (i == (currentRow.size() - 1)) {
+ printTableBorder(" │");
+ }
+ }
+ out.println();
+ out.flush();
+ wrappedLine++;
+ } while (hasMoreWrappedLines);
+
+ currentRow.clear();
+ }
+
+ @Override
+ public void writeEndRowInternal() {
+ // already done – wrapped row ends
+ }
+
+ private static String[] wrapLines(String s, int width, boolean rightAlign) {
+ String[] array = new String[(s.length() - 1) / width + 1];
+ for (int i = 0; i < array.length; i++) {
+ if (i == array.length - 1) {
+ String part = s.substring(i * width, s.length());
+ array[i] = rightAlign ? lpad(part, width) : rpad(part, width);
+ } else {
+ array[i] = s.substring(i * width, (i + 1) * width);
+ }
+ }
+ return array;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/formatting/TeXFormatter.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,208 @@
+/**
+ * SQL-DK
+ * Copyright © 2014 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.formatting;
+
+import info.globalcode.sql.dk.ColorfulPrintWriter;
+import info.globalcode.sql.dk.Constants;
+import info.globalcode.sql.dk.configuration.PropertyDeclaration;
+import static info.globalcode.sql.dk.formatting.CommonProperties.COLORFUL;
+import static info.globalcode.sql.dk.formatting.CommonProperties.COLORFUL_DESCRIPTION;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Outputs result sets in (La)TeX format.
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+@PropertyDeclaration(name = COLORFUL, defaultValue = "false", type = Boolean.class, description = COLORFUL_DESCRIPTION)
+public class TeXFormatter extends AbstractFormatter {
+
+ public static final String NAME = "tex"; // bash-completion:formatter
+ private static final ColorfulPrintWriter.TerminalColor COMMAND_COLOR = ColorfulPrintWriter.TerminalColor.Magenta;
+ private static final ColorfulPrintWriter.TerminalColor OPTIONS_COLOR = ColorfulPrintWriter.TerminalColor.Yellow;
+ private static final Map<Character, String> TEX_ESCAPE_MAP;
+ private final ColorfulPrintWriter out;
+
+ static {
+ Map<Character, String> replacements = new HashMap<>();
+
+ replacements.put('\\', "\\textbackslash{}");
+ replacements.put('{', "\\{{}");
+ replacements.put('}', "\\}{}");
+ replacements.put('_', "\\_{}");
+ replacements.put('^', "\\textasciicircum{}");
+ replacements.put('#', "\\#{}");
+ replacements.put('&', "\\&{}");
+ replacements.put('$', "\\${}");
+ replacements.put('%', "\\%{}");
+ replacements.put('~', "\\textasciitilde{}");
+ replacements.put('-', "{-}");
+
+ TEX_ESCAPE_MAP = Collections.unmodifiableMap(replacements);
+ }
+
+ public TeXFormatter(FormatterContext formatterContext) {
+ super(formatterContext);
+ boolean colorful = formatterContext.getProperties().getBoolean(COLORFUL, false);
+ out = new ColorfulPrintWriter(formatterContext.getOutputStream(), false, colorful);
+ }
+
+ @Override
+ public void writeStartBatch() {
+ super.writeStartBatch();
+
+ printCommand("documentclass", "a4paper,twoside", "article", true);
+ printCommand("usepackage", "T1", "fontenc", true);
+ printCommand("usepackage", "utf8x", "inputenc", true);
+ printCommand("usepackage", "pdfauthor={" + Constants.WEBSITE + "}, bookmarks=true,unicode,colorlinks=true,linkcolor=black,urlcolor=blue,citecolor=blue", "hyperref", true);
+ printBegin("document");
+ }
+
+ @Override
+ public void writeEndBatch() {
+ super.writeEndBatch();
+ printEnd("document");
+ }
+
+ @Override
+ public void writeColumnValue(Object value) {
+ super.writeColumnValue(value);
+ // TODO: arrays, numbers, booleans, nulls etc.:
+ out.print(escapeTex(toString(value)));
+
+ if (!isCurrentColumnLast()) {
+ printColumnSeparator();
+ }
+ }
+
+ @Override
+ public void writeEndRow() {
+ super.writeEndRow();
+ printEndRow();
+ }
+
+ @Override
+ public void writeStartResultSet(ColumnsHeader header) {
+ super.writeStartResultSet(header);
+ printCommand("begin", null, "tabular", false);
+
+ List<ColumnDescriptor> columnDescriptors = header.getColumnDescriptors();
+
+ StringBuilder columnAlignments = new StringBuilder();
+ for (ColumnDescriptor cd : columnDescriptors) {
+ if (cd.isNumeric() || cd.isBoolean()) {
+ columnAlignments.append('r');
+ } else {
+ columnAlignments.append('l');
+ }
+ }
+
+ printCommand(null, null, columnAlignments.toString(), true);
+ printCommand("hline", null, null, true);
+
+ for (ColumnDescriptor cd : columnDescriptors) {
+ printCommand("textbf", null, cd.getLabel(), false);
+ if (cd.isLastColumn()) {
+ printEndRow();
+ } else {
+ printColumnSeparator();
+ }
+ }
+
+ printCommand("hline", null, null, true);
+ }
+
+ @Override
+ public void writeEndResultSet() {
+ super.writeEndResultSet();
+ printCommand("hline", null, null, true);
+ printEnd("tabular");
+ }
+
+ private String escapeTex(String text) {
+ if (text == null) {
+ return null;
+ } else {
+ StringBuilder result = new StringBuilder(text.length() * 2);
+
+ for (char ch : text.toCharArray()) {
+ String replacement = TEX_ESCAPE_MAP.get(ch);
+ result.append(replacement == null ? ch : replacement);
+ }
+
+ return result.toString();
+ }
+ }
+
+ protected String toString(Object value) {
+ return String.valueOf(value);
+ }
+
+ private void printColumnSeparator() {
+ out.print(COMMAND_COLOR, " & ");
+ }
+
+ private void printEndRow() {
+ out.println(COMMAND_COLOR, " \\\\");
+ out.flush();
+ }
+
+ /**
+ *
+ * @param command will not be escaped – should contain just a valid TeX command name
+ * @param options will not be escaped – should be properly formatted to be printed inside [
+ * and ]
+ * @param value will be escaped
+ * @param println whether to print line end and flush
+ */
+ private void printCommand(String command, String options, String value, boolean println) {
+
+ if (command != null) {
+ out.print(COMMAND_COLOR, "\\" + command);
+ }
+
+ if (options != null) {
+ out.print(COMMAND_COLOR, "[");
+ out.print(OPTIONS_COLOR, options);
+ out.print(COMMAND_COLOR, "]");
+ }
+
+ if (value != null) {
+ out.print(COMMAND_COLOR, "{");
+ out.print(escapeTex(value));
+ out.print(COMMAND_COLOR, "}");
+ }
+
+ if (println) {
+ out.println();
+ out.flush();
+ }
+ }
+
+ private void printBegin(String environment) {
+ printCommand("begin", null, environment, true);
+ }
+
+ private void printEnd(String environment) {
+ printCommand("end", null, environment, true);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/formatting/XhtmlFormatter.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,262 @@
+/**
+ * SQL-DK
+ * Copyright © 2014 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.formatting;
+
+import info.globalcode.sql.dk.Constants;
+import info.globalcode.sql.dk.NamedParameter;
+import info.globalcode.sql.dk.Parameter;
+import info.globalcode.sql.dk.Xmlns;
+import info.globalcode.sql.dk.configuration.DatabaseDefinition;
+import info.globalcode.sql.dk.configuration.Properties;
+import info.globalcode.sql.dk.configuration.Property;
+import static info.globalcode.sql.dk.formatting.AbstractXmlFormatter.qname;
+import java.sql.Array;
+import java.sql.SQLException;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.xml.namespace.QName;
+
+/**
+ * Prints result sets and parameters as tables, SQL as preformatted and updates counts as
+ * paragraphs. You can pick XHTML fragments (usually tabular data) and use it on your website or use
+ * whole output as preview or report.
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class XhtmlFormatter extends AbstractXmlFormatter {
+
+ private static final Logger log = Logger.getLogger(XhtmlFormatter.class.getName());
+ public static final String NAME = "xhtml"; // bash-completion:formatter
+ private static final String DOCTYPE = "html PUBLIC \"-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN\" \"http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd\"";
+ private static final String CSS_FILE = "info/globalcode/sql/dk/formatter/XhtmlFormatter.css";
+ private int statementCounter = 0;
+ private int resultSetCounter = 0;
+ private int updatesResultCounter = 0;
+
+ public XhtmlFormatter(FormatterContext formatterContext) {
+ super(addDefaults(formatterContext));
+ }
+
+ /**
+ * Do not indent text – preserve whitespace for pre elements
+ */
+ private static FormatterContext addDefaults(FormatterContext formatterContext) {
+ Properties defaults = new Properties(1);
+ defaults.add(new Property(PROPERTY_INDENT_TEXT, "false"));
+ formatterContext.getProperties().setLastDefaults(defaults);
+ return formatterContext;
+ }
+
+ @Override
+ public void writeStartBatch() {
+ super.writeStartBatch();
+ printStartDocument();
+ printDoctype(DOCTYPE);
+ printStartElement(qname("html"), singleAttribute(qname("xmlns"), Xmlns.XHTML));
+
+ printStartElement(qname("head"));
+ printTextElement(qname("title"), null, Constants.PROGRAM_NAME + ": batch results");
+ printCss();
+ printEndElement();
+
+ printStartElement(qname("body"));
+ }
+
+ private void printCss() {
+
+ try (Scanner css = new Scanner(getClass().getClassLoader().getResourceAsStream(CSS_FILE))) {
+ printStartElement(qname("style"), singleAttribute(qname("type"), "text/css"));
+ while (css.hasNext()) {
+ printText(css.nextLine(), true);
+ }
+ printEndElement();
+ }
+ }
+
+ @Override
+ public void writeEndBatch() {
+ super.writeEndBatch();
+ printEndElement();
+ printEndElement();
+ printEndDocument();
+ }
+
+ @Override
+ public void writeStartDatabase(DatabaseDefinition databaseDefinition) {
+ super.writeStartDatabase(databaseDefinition);
+ printTextElement(qname("h1"), null, "Database: " + databaseDefinition.getName());
+
+ printStartElement(qname("p"));
+ printText("This is XHTML output of batch executed at: ", true);
+ printText(new Date().toString(), true);
+ printEndElement();
+ }
+
+ @Override
+ public void writeQuery(String sql) {
+ super.writeQuery(sql);
+ printTextElement(qname("pre"), null, sql);
+ }
+
+ @Override
+ public void writeParameters(List<? extends Parameter> parameters) {
+ super.writeParameters(parameters);
+
+ if (parameters == null || parameters.isEmpty()) {
+ printTextElement(qname("p"), null, "(this query has no parameters)");
+ } else {
+ printTextElement(qname("h3"), null, "Parameters:");
+
+ printStartElement(qname("table"));
+
+ printStartElement(qname("thead"));
+ printStartElement(qname("tr"));
+ printTextElement(qname("td"), null, "id");
+ printTextElement(qname("td"), null, "type");
+ printTextElement(qname("td"), null, "value");
+ printEndElement();
+ printEndElement();
+
+ printStartElement(qname("tbody"));
+ for (int i = 0; i < parameters.size(); i++) {
+ Parameter p = parameters.get(i);
+ printStartElement(qname("tr"));
+ String numberOrName;
+ if (p instanceof NamedParameter) {
+ numberOrName = ((NamedParameter) p).getName();
+ } else {
+ numberOrName = String.valueOf(i + 1);
+ }
+ printTextElement(qname("td"), null, numberOrName);
+ printTextElement(qname("td"), null, p.getType().name());
+ printTableData(p.getValue());
+ printEndElement();
+ }
+ printEndElement();
+
+ printEndElement();
+ }
+ }
+
+ private void printTableData(Object value) {
+
+ if (value instanceof Array) {
+ Array sqlArray = (Array) value;
+ try {
+ Object[] array = (Object[]) sqlArray.getArray();
+ printStartElement(qname("td"));
+ printArray(array);
+ printEndElement();
+ } catch (SQLException e) {
+ log.log(Level.SEVERE, "Unable to format array", e);
+ printTableData(String.valueOf(value));
+ }
+ } else {
+ Map<QName, String> attributes = null;
+ if (value instanceof Number) {
+ attributes = singleAttribute(qname("class"), "number");
+ } else if (value instanceof Boolean) {
+ attributes = singleAttribute(qname("class"), "boolean");
+ }
+ printTextElement(qname("td"), attributes, String.valueOf(value));
+ }
+ }
+
+ private void printArray(Object[] array) {
+ printStartElement(qname("ul"));
+ for (Object o : array) {
+ if (o instanceof Object[]) {
+ printStartElement(qname("li"));
+ printTextElement(qname("p"), null, "nested array:");
+ printArray((Object[]) o);
+ printEndElement();
+ } else {
+ printTextElement(qname("li"), null, String.valueOf(o));
+ }
+ }
+ printEndElement();
+ }
+
+ @Override
+ public void writeStartResultSet(ColumnsHeader header) {
+ super.writeStartResultSet(header);
+ resultSetCounter++;
+ printEmptyElement(qname("hr"), null);
+ printTextElement(qname("h3"), null, "Result set #" + resultSetCounter);
+ printStartElement(qname("table"));
+ printStartElement(qname("thead"));
+ printStartElement(qname("tr"));
+ for (ColumnDescriptor cd : header.getColumnDescriptors()) {
+ // TODO: type
+ printTextElement(qname("td"), null, cd.getLabel());
+ }
+ printEndElement();
+ printEndElement();
+
+ printStartElement(qname("tbody"));
+ }
+
+ @Override
+ public void writeEndResultSet() {
+ super.writeEndResultSet();
+ printEndElement();
+ printEndElement();
+ printTextElement(qname("p"), null, "Record count: " + getCurrentRowCount());
+ }
+
+ @Override
+ public void writeStartRow() {
+ super.writeStartRow();
+ printStartElement(qname("tr"));
+ }
+
+ @Override
+ public void writeColumnValue(Object value) {
+ super.writeColumnValue(value);
+ printTableData(value);
+ }
+
+ @Override
+ public void writeEndRow() {
+ super.writeEndRow();
+ printEndElement();
+ }
+
+ @Override
+ public void writeStartStatement() {
+ super.writeStartStatement();
+ statementCounter++;
+ printEmptyElement(qname("hr"), null);
+ printTextElement(qname("h2"), null, "SQL statement #" + statementCounter);
+ resultSetCounter = 0;
+ updatesResultCounter = 0;
+ }
+
+ @Override
+ public void writeUpdatesResult(int updatedRowsCount) {
+ super.writeUpdatesResult(updatedRowsCount);
+ updatesResultCounter++;
+ printEmptyElement(qname("hr"), null);
+ printTextElement(qname("h3"), null, "Updates result #" + updatesResultCounter);
+ printTextElement(qname("p"), null, "Updated rows: " + updatedRowsCount);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/formatting/XmlFormatter.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,245 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.formatting;
+
+import info.globalcode.sql.dk.Parameter;
+import info.globalcode.sql.dk.Xmlns;
+import info.globalcode.sql.dk.configuration.DatabaseDefinition;
+import static info.globalcode.sql.dk.Functions.notNull;
+import info.globalcode.sql.dk.NamedParameter;
+import info.globalcode.sql.dk.configuration.PropertyDeclaration;
+import static info.globalcode.sql.dk.formatting.AbstractXmlFormatter.qname;
+import java.sql.Array;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.SQLXML;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.xml.namespace.QName;
+
+/**
+ * <p>
+ * Prints machine-readable output – XML document containing resultsets and updates count. Good
+ * choice for further processing – e.g. XSL transformation.</p>
+ *
+ * <p>
+ * TODO: XSD</p>
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+@PropertyDeclaration(name = XmlFormatter.PROPERTY_LABELED_COLUMNS, defaultValue = "false", type = Boolean.class, description = "whether to add 'label' attribute to each 'column' element")
+public class XmlFormatter extends AbstractXmlFormatter {
+
+ public static final String NAME = "xml"; // bash-completion:formatter
+ public static final String PROPERTY_LABELED_COLUMNS = "labeledColumns";
+ private static final Logger log = Logger.getLogger(XmlFormatter.class.getName());
+ private final boolean labeledColumns;
+
+ public XmlFormatter(FormatterContext formatterContext) {
+ super(formatterContext);
+ labeledColumns = formatterContext.getProperties().getBoolean(PROPERTY_LABELED_COLUMNS, false);
+ }
+
+ @Override
+ public void writeStartBatch() {
+ super.writeStartBatch();
+ printStartDocument();
+ printStartElement(qname("batchResult"), singleAttribute(qname("xmlns"), Xmlns.BATCH_RESULT));
+ }
+
+ @Override
+ public void writeEndBatch() {
+ super.writeEndBatch();
+ printEndElement();
+ printEndDocument();
+ }
+
+ @Override
+ public void writeStartDatabase(DatabaseDefinition databaseDefinition) {
+ super.writeStartDatabase(databaseDefinition);
+ Map<QName, String> attributes = databaseDefinition.getName() == null ? null : singleAttribute(qname("name"), databaseDefinition.getName());
+ printStartElement(qname("database"), attributes);
+ }
+
+ @Override
+ public void writeEndDatabase() {
+ super.writeEndDatabase();
+ printEndElement();
+ }
+
+ @Override
+ public void writeStartStatement() {
+ super.writeStartStatement();
+ printStartElement(qname("statement"));
+ }
+
+ @Override
+ public void writeEndStatement() {
+ super.writeEndStatement();
+ printEndElement();
+ }
+
+ @Override
+ public void writeQuery(String sql) {
+ super.writeQuery(sql);
+ printTextElement(qname("sql"), null, sql);
+ }
+
+ @Override
+ public void writeParameters(List<? extends Parameter> parameters) {
+ super.writeParameters(parameters);
+
+ for (Parameter p : notNull(parameters)) {
+
+ Map<QName, String> attributes = new LinkedHashMap<>(2);
+ if (p instanceof NamedParameter) {
+ attributes.put(qname("name"), ((NamedParameter) p).getName());
+ }
+ attributes.put(qname("type"), p.getType().name());
+
+ printTextElement(qname("parameter"), attributes, String.valueOf(p.getValue()));
+ }
+
+ }
+
+ @Override
+ public void writeStartResultSet(ColumnsHeader header) {
+ super.writeStartResultSet(header);
+ printStartElement(qname("resultSet"));
+
+ for (ColumnDescriptor cd : header.getColumnDescriptors()) {
+ Map<QName, String> attributes = new LinkedHashMap<>(4);
+ attributes.put(qname("label"), cd.getLabel());
+ attributes.put(qname("name"), cd.getName());
+ attributes.put(qname("typeName"), cd.getTypeName());
+ attributes.put(qname("type"), String.valueOf(cd.getType()));
+ printEmptyElement(qname("columnHeader"), attributes);
+ }
+ }
+
+ @Override
+ public void writeEndResultSet() {
+ super.writeEndResultSet();
+ printEndElement();
+ }
+
+ @Override
+ public void writeStartRow() {
+ super.writeStartRow();
+ printStartElement(qname("row"));
+ }
+
+ @Override
+ public void writeColumnValue(Object value) {
+ super.writeColumnValue(value);
+
+ Map<QName, String> attributes = null;
+ if (labeledColumns) {
+ attributes = new LinkedHashMap<>(2);
+ attributes.put(qname("label"), getCurrentColumnsHeader().getColumnDescriptors().get(getCurrentColumnsCount() - 1).getLabel());
+ }
+
+ if (value == null) {
+ if (attributes == null) {
+ attributes = new LinkedHashMap<>(2);
+ }
+ attributes.put(qname("null"), "true");
+ printEmptyElement(qname("column"), attributes);
+ } else if (value instanceof Array) {
+
+ Array sqlArray = (Array) value;
+ try {
+ Object[] array = (Object[]) sqlArray.getArray();
+ printStartElement(qname("column"), attributes);
+ printArray(array);
+ printEndElement();
+ } catch (SQLException e) {
+ // FIXME: rewrite array formatting, remember array mode, don't try sqlArray.getArray() again and again if it has failed
+ log.log(Level.SEVERE, "Unable to format array", e);
+ try {
+ ResultSet arrayResultSet = sqlArray.getResultSet();
+ //int columnCount = arrayResultSet.getMetaData().getColumnCount();
+ ArrayList<Object> arrayList = new ArrayList<>();
+ while (arrayResultSet.next()) {
+ arrayList.add(arrayResultSet.getObject(2));
+ // for (int i = 1; i <= columnCount; i++) {
+ // log.log(Level.INFO, "Array column {0} = {1}", new Object[]{i, arrayResultSet.getObject(i)});
+ // }
+ }
+
+ printStartElement(qname("column"), attributes);
+ // FIXME: instanceof SQLXML, see below
+ printArray(arrayList.toArray());
+ printEndElement();
+
+ } catch (SQLException e2) {
+ // FIXME: fix logging, error recovery
+ log.log(Level.SEVERE, "Second level fuck up !!!", e2);
+ }
+
+ writeColumnValue(String.valueOf(value));
+ }
+
+ } else if (value instanceof SQLXML) { // FIXME: move to separate method, to AbstractFormatter?
+ SQLXML xml = (SQLXML) value;
+ // TODO: parse DOM/SAX and transplant XML, don't escape (optional)
+ try {
+ printTextElement(qname("column"), attributes, xml.getString());
+ } catch (SQLException e) {
+ log.log(Level.SEVERE, "Unable to format XML", e);
+ writeColumnValue(String.valueOf(value));
+ }
+ } else {
+ printTextElement(qname("column"), attributes, toString(value));
+ }
+ }
+
+ private void printArray(Object[] array) {
+ printStartElement(qname("array"));
+ for (Object o : array) {
+ if (o instanceof Object[]) {
+ printStartElement(qname("item"));
+ printArray((Object[]) o);
+ printEndElement();
+ } else {
+ printTextElement(qname("item"), null, String.valueOf(o));
+ }
+ }
+ printEndElement();
+ }
+
+ @Override
+ public void writeEndRow() {
+ super.writeEndRow();
+ printEndElement();
+ }
+
+ @Override
+ public void writeUpdatesResult(int updatedRowsCount) {
+ super.writeUpdatesResult(updatedRowsCount);
+ printTextElement(qname("updatedRows"), null, String.valueOf(updatedRowsCount));
+ }
+
+ protected String toString(Object value) {
+ return String.valueOf(value);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/jmx/ConnectionManagement.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,97 @@
+/**
+ * SQL-DK
+ * Copyright © 2014 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.jmx;
+
+import java.util.EnumMap;
+import java.util.Map;
+
+/**
+ * JMX management bean for progress reporting.
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class ConnectionManagement implements ConnectionManagementMBean {
+
+ private final String databaseName;
+ private final Map<COUNTER, Integer> counters = new EnumMap(COUNTER.class);
+
+ public ConnectionManagement(String databaseName) {
+ this.databaseName = databaseName;
+ for (COUNTER c : COUNTER.values()) {
+ counters.put(c, 0);
+ }
+ }
+
+ public enum COUNTER {
+
+ COMMAND,
+ RECORD_CURRENT,
+ RECORD_TOTAL
+ };
+
+ public void incrementCounter(COUNTER counter) {
+ synchronized (counters) {
+ int old = counters.get(counter);
+ counters.put(counter, old + 1);
+ }
+ }
+
+ public void resetCounter(COUNTER counter) {
+ synchronized (counters) {
+ counters.put(counter, 0);
+ }
+ }
+
+ public static void incrementCounter(ConnectionManagement mbean, COUNTER counter) {
+ if (mbean != null) {
+ mbean.incrementCounter(counter);
+ }
+ }
+
+ public static void resetCounter(ConnectionManagement mbean, COUNTER counter) {
+ if (mbean != null) {
+ mbean.resetCounter(counter);
+ }
+ }
+
+ @Override
+ public String getDatabaseName() {
+ return databaseName;
+ }
+
+ @Override
+ public int getCommandCount() {
+ synchronized (counters) {
+ return counters.get(COUNTER.COMMAND);
+ }
+ }
+
+ @Override
+ public int getCurrentRecordCount() {
+ synchronized (counters) {
+ return counters.get(COUNTER.RECORD_CURRENT);
+ }
+ }
+
+ @Override
+ public int getTotalRecordCount() {
+ synchronized (counters) {
+ return counters.get(COUNTER.RECORD_TOTAL);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/jmx/ConnectionManagementMBean.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,34 @@
+/**
+ * SQL-DK
+ * Copyright © 2014 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.jmx;
+
+/**
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public interface ConnectionManagementMBean {
+
+ public String getDatabaseName();
+
+ public int getCommandCount();
+
+ public int getCurrentRecordCount();
+
+ public int getTotalRecordCount();
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/jmx/ManagementUtils.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,68 @@
+/**
+ * SQL-DK
+ * Copyright © 2014 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.jmx;
+
+import java.lang.management.ManagementFactory;
+import java.util.Hashtable;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+/**
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class ManagementUtils {
+
+ private static final Logger log = Logger.getLogger(ManagementUtils.class.getName());
+ public static final String DEFAULT_CONNECTION_JMX_NAME = "main";
+
+ /**
+ * @see #registerMBean(java.lang.String, java.lang.String) with default JMX name
+ */
+ public static ConnectionManagement registerMBean(String dbName) {
+ return registerMBean(dbName, DEFAULT_CONNECTION_JMX_NAME);
+ }
+
+ /**
+ *
+ * @param dbName database name
+ * @param jmxName name of JMX bean
+ * @return registered JMX bean | or null if registration fails (should not)
+ */
+ public static ConnectionManagement registerMBean(String dbName, String jmxName) {
+ try {
+ ConnectionManagement mbean = new ConnectionManagement(dbName);
+ MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+ Hashtable<String, String> objectProperties = new Hashtable<>();
+ objectProperties.put("type", "Connection");
+ objectProperties.put("name", jmxName);
+ ObjectName objectName = new ObjectName("info.globalcode.sql.dk", objectProperties);
+ mbs.registerMBean(mbean, objectName);
+ log.log(Level.FINE, "JMX MBean was registered as: {0}", objectName);
+ return mbean;
+ } catch (Exception e) {
+ log.log(Level.WARNING, "Unable to register JMX MBean", e);
+ return null;
+ }
+ }
+
+ private ManagementUtils() {
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/logging/ColorfulConsoleFormatter.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,97 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.logging;
+
+import info.globalcode.sql.dk.ColorfulPrintWriter;
+import static info.globalcode.sql.dk.ColorfulPrintWriter.TerminalColor;
+import static info.globalcode.sql.dk.ColorfulPrintWriter.TerminalStyle;
+import static info.globalcode.sql.dk.Functions.rpad;
+import java.io.StringWriter;
+import java.util.logging.Formatter;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+
+/**
+ * For console/terminal log output. Log messages are printed in brief and colorful form.
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class ColorfulConsoleFormatter extends Formatter {
+
+ private boolean printStacktrace = false;
+
+ @Override
+ public String format(LogRecord r) {
+ StringWriter sw = new StringWriter();
+ try (ColorfulPrintWriter out = new ColorfulPrintWriter(sw)) {
+ printLevel(out, r.getLevel());
+ printMessage(out, r);
+ printThrowable(out, r);
+ out.println();
+ }
+ return sw.toString();
+ }
+
+ private void printLevel(ColorfulPrintWriter out, Level l) {
+ TerminalColor color = TerminalColor.Magenta;
+
+ if (l == Level.SEVERE) {
+ color = TerminalColor.Red;
+ } else if (l == Level.WARNING) {
+ color = TerminalColor.Yellow;
+ }
+
+ out.print(color, rpad(l.getLocalizedName() + ": ", 10));
+ }
+
+ private void printMessage(ColorfulPrintWriter out, LogRecord r) {
+ out.print(formatMessage(r));
+ }
+
+ private void printThrowable(ColorfulPrintWriter out, LogRecord r) {
+ Throwable t = r.getThrown();
+ if (t != null) {
+ out.print(": ");
+ out.print(TerminalColor.Red, t.getClass().getSimpleName());
+ String message = t.getLocalizedMessage();
+ if (message != null) {
+ out.print(": ");
+ if (printStacktrace) {
+ out.print(message);
+ } else {
+ out.print(message.replaceAll("\\n", " "));
+ }
+ }
+ if (printStacktrace) {
+ out.println();
+ out.setForegroundColor(TerminalColor.Yellow);
+ out.setStyle(TerminalStyle.Dim);
+ t.printStackTrace(out);
+ out.resetAll();
+ }
+ }
+ }
+
+ public boolean isPrintStacktrace() {
+ return printStacktrace;
+ }
+
+ public void setPrintStacktrace(boolean printStacktrace) {
+ this.printStacktrace = printStacktrace;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/logging/LoggerInitializer.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,78 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.logging;
+
+import info.globalcode.sql.dk.Constants;
+import java.util.logging.ConsoleHandler;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Configures logging subsystem.
+ * Usage: java -Djava.util.logging.config.class=info.globalcode.sql.dk.logging.LoggerInitializer …
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class LoggerInitializer {
+
+ private static final Logger log = Logger.getLogger(LoggerInitializer.class.getName());
+ public static final String LEVEL_PROPERTY = LoggerInitializer.class.getName() + ".level";
+ private static final Level DEFAULT_LEVEL = Level.INFO;
+
+ public LoggerInitializer() {
+ Logger logger = Logger.getLogger(Constants.JAVA_PACKAGE);
+ ConsoleHandler handler = new ConsoleHandler();
+ ColorfulConsoleFormatter formatter = new ColorfulConsoleFormatter();
+
+ logger.addHandler(handler);
+ handler.setFormatter(formatter);
+
+ setLevel(logger, handler, formatter);
+
+
+ /**
+ * TODO: optional FileHandler – detailed logs in file in ~/sql-dk/log/…
+ */
+ }
+
+ private void setLevel(Logger logger, Handler handler, ColorfulConsoleFormatter formatter) {
+ boolean levelParseError = false;
+ Level level;
+ String cliLevel = System.getProperty(LEVEL_PROPERTY);
+ if (cliLevel == null) {
+ level = DEFAULT_LEVEL;
+ } else {
+ try {
+ level = Level.parse(cliLevel);
+ } catch (IllegalArgumentException e) {
+ level = DEFAULT_LEVEL;
+ levelParseError = true;
+ }
+ }
+
+ handler.setLevel(level);
+ logger.setLevel(level);
+
+ if (levelParseError) {
+ log.log(Level.WARNING, "Invalid logging level „{0}“ specified in „{1}“ → using default level „{2}“", new Object[]{cliLevel, LEVEL_PROPERTY, DEFAULT_LEVEL});
+ }
+
+ formatter.setPrintStacktrace(level.intValue() < Level.INFO.intValue());
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/logging/LoggerProducer.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,36 @@
+/**
+ * SQL-DK
+ * Copyright © 2015 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk.logging;
+
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class LoggerProducer {
+
+ /**
+ * @return created logger for the caller class
+ */
+ public static Logger getLogger() {
+ String className = Thread.currentThread().getStackTrace()[2].getClassName();
+ return Logger.getLogger(className);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/resources/info/globalcode/sql/dk/configuration/jaxb.index Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,1 @@
+Configuration
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/resources/info/globalcode/sql/dk/example-config.xml Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,1 @@
+../../../../../../../../../xml/config.xml
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/resources/info/globalcode/sql/dk/formatter/XhtmlFormatter.css Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,54 @@
+body {
+ font-family: sans-serif;
+ font-size: 16px;
+ padding-left: 16px;
+ padding-right: 16px;
+}
+
+pre {
+ background-color: #ddd;
+ padding: 6px;
+ border-radius: 4px;
+ overflow: auto;
+
+ -moz-tab-size: 4;
+ -o-tab-size: 4;
+ tab-size: 4;
+}
+
+table {
+ border-collapse:collapse;
+ box-shadow: 3px 3px 3px grey;
+ margin-top: 10px;
+ margin-bottom: 20px;
+}
+td, th {
+ border: 1px solid black;
+ padding-top: 4px;
+ padding-bottom: 4px;
+ padding-left: 6px;
+ padding-right: 6px;
+ font-weight: normal;
+}
+td.number {
+ text-align: right;
+}
+td.boolean {
+ text-align: right;
+}
+thead tr {
+ background: #ddd;
+ color:black;
+}
+tbody tr:hover {
+ background-color: #eee;
+ color:black;
+}
+
+table ul {
+ margin: 0px;
+}
+
+table li {
+ padding-right: 10px;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/main/resources/info/globalcode/sql/dk/license.txt Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,1 @@
+../../../../../../../../../license/gpl.txt
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/test/java/info/globalcode/sql/dk/CLIParserTest.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,195 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk;
+
+import info.globalcode.sql.dk.CLIParser.Tokens;
+import static info.globalcode.sql.dk.CLIParser.TYPE_NAME_SEPARATOR;
+import info.globalcode.sql.dk.InfoLister.InfoType;
+import java.io.ByteArrayInputStream;
+import java.util.Collection;
+import static org.testng.Assert.*;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class CLIParserTest {
+
+ private static final String DATABASE_NAME_1 = "some database 1";
+ private static final String SQL_1 = "SELECT * FROM table1";
+ private static final String DATA_1 = "aaa";
+ private static final String DATA_2 = "bbb";
+ private static final String DATA_3 = "ccc";
+ private static final String NAME_1 = "param1";
+ private static final String NAME_2 = "param2";
+ private static final String NAME_3 = "param3";
+ private CLIParser parser;
+
+ @BeforeMethod
+ public void setUpMethod() throws Exception {
+ parser = new CLIParser();
+ }
+
+ private CLIOptions parseOptions(String[] args) throws CLIParserException {
+ return parser.parseOptions(args, new ByteArrayInputStream("".getBytes()));
+ }
+
+ @Test
+ public void testParseOptions_QueryNow_NoParams() throws InvalidOptionsException, CLIParserException {
+ String[] args = new String[]{
+ Tokens.DB, DATABASE_NAME_1,
+ Tokens.SQL, SQL_1};
+ CLIOptions options = parseOptions(args);
+ options.validate();
+
+ assertEquals(options.getDatabaseName(), DATABASE_NAME_1);
+ assertEquals(options.getSql(), SQL_1);
+ assertEquals(options.getMode(), CLIOptions.MODE.QUERY_NOW);
+ assertTrue(options.getNamedParameters().isEmpty(), "Named parameters should be empty.");
+ assertTrue(options.getNumberedParameters().isEmpty(), "Numbered parameters should be empty.");
+ }
+
+ @Test
+ public void testParseOptions_QueryNow_Numbered() throws InvalidOptionsException, CLIParserException {
+ String[] args = new String[]{
+ Tokens.DB, DATABASE_NAME_1,
+ Tokens.SQL, SQL_1,
+ Tokens.DATA, DATA_1, DATA_2, DATA_3};
+ CLIOptions options = parseOptions(args);
+ options.validate();
+
+ assertEquals(options.getDatabaseName(), DATABASE_NAME_1);
+ assertEquals(options.getSql(), SQL_1);
+ assertEquals(options.getMode(), CLIOptions.MODE.QUERY_NOW);
+ assertEquals(options.getNumberedParameters().size(), 3);
+ assertEquals(options.getNumberedParameters().get(0).getValue(), DATA_1);
+ assertEquals(options.getNumberedParameters().get(1).getValue(), DATA_2);
+ assertEquals(options.getNumberedParameters().get(2).getValue(), DATA_3);
+ assertEquals(options.getNumberedParameters().get(0).getType(), Parameter.DEFAULT_TYPE);
+ assertEquals(options.getNumberedParameters().get(1).getType(), Parameter.DEFAULT_TYPE);
+ assertEquals(options.getNumberedParameters().get(2).getType(), Parameter.DEFAULT_TYPE);
+ }
+
+ @Test
+ public void testParseOptions_QueryNow_Numbered_withTypes() throws InvalidOptionsException, CLIParserException {
+ String[] args = new String[]{
+ Tokens.DB, DATABASE_NAME_1,
+ Tokens.SQL, SQL_1,
+ Tokens.TYPES, " INTEGER,VARCHAR, BOOLEAN",
+ Tokens.DATA, DATA_1, DATA_2, DATA_3};
+ CLIOptions options = parseOptions(args);
+ options.validate();
+
+ assertEquals(options.getDatabaseName(), DATABASE_NAME_1);
+ assertEquals(options.getSql(), SQL_1);
+ assertEquals(options.getMode(), CLIOptions.MODE.QUERY_NOW);
+ assertEquals(options.getNumberedParameters().size(), 3);
+ assertEquals(options.getNumberedParameters().get(0).getValue(), DATA_1);
+ assertEquals(options.getNumberedParameters().get(1).getValue(), DATA_2);
+ assertEquals(options.getNumberedParameters().get(2).getValue(), DATA_3);
+ assertEquals(options.getNumberedParameters().get(0).getType(), SQLType.INTEGER);
+ assertEquals(options.getNumberedParameters().get(1).getType(), SQLType.VARCHAR);
+ assertEquals(options.getNumberedParameters().get(2).getType(), SQLType.BOOLEAN);
+ }
+
+ @Test
+ public void testParseOptions_QueryNow_Named() throws InvalidOptionsException, CLIParserException {
+ String[] args = new String[]{
+ Tokens.DB, DATABASE_NAME_1,
+ Tokens.SQL, SQL_1,
+ Tokens.DATA_NAMED, NAME_1, DATA_1, NAME_2, DATA_2, NAME_3, DATA_3};
+ CLIOptions options = parseOptions(args);
+ options.validate();
+
+ assertEquals(options.getDatabaseName(), DATABASE_NAME_1);
+ assertEquals(options.getSql(), SQL_1);
+ assertEquals(options.getMode(), CLIOptions.MODE.QUERY_NOW);
+ assertEquals(options.getNamedParameters().size(), 3);
+ assertNamedParameter(options.getNamedParameters(), NAME_1, DATA_1, Parameter.DEFAULT_TYPE);
+ assertNamedParameter(options.getNamedParameters(), NAME_2, DATA_2, Parameter.DEFAULT_TYPE);
+ assertNamedParameter(options.getNamedParameters(), NAME_3, DATA_3, Parameter.DEFAULT_TYPE);
+ }
+
+ @Test
+ public void testParseOptions_QueryNow_Named_withTypes() throws InvalidOptionsException, CLIParserException {
+ String[] args = new String[]{
+ Tokens.DB, DATABASE_NAME_1,
+ Tokens.SQL, SQL_1,
+ Tokens.NAME_PREFIX, "$",
+ Tokens.TYPES, " " + NAME_1 + TYPE_NAME_SEPARATOR + "INTEGER" + "," + NAME_3 + TYPE_NAME_SEPARATOR + "BOOLEAN",
+ Tokens.DATA_NAMED, NAME_1, DATA_1, NAME_2, DATA_2, NAME_3, DATA_3};
+ CLIOptions options = parseOptions(args);
+ options.validate();
+
+ assertEquals(options.getDatabaseName(), DATABASE_NAME_1);
+ assertEquals(options.getSql(), SQL_1);
+ assertEquals(options.getMode(), CLIOptions.MODE.QUERY_NOW);
+ assertEquals(options.getNamedParameters().size(), 3);
+ assertNamedParameter(options.getNamedParameters(), NAME_1, DATA_1, SQLType.INTEGER);
+ assertNamedParameter(options.getNamedParameters(), NAME_2, DATA_2, Parameter.DEFAULT_TYPE);
+ assertNamedParameter(options.getNamedParameters(), NAME_3, DATA_3, SQLType.BOOLEAN);
+ }
+
+ private void assertNamedParameter(Collection<NamedParameter> params, String name, Object value, SQLType type) {
+ for (NamedParameter p : params) {
+ if (name.equals(p.getName())) {
+ assertEquals(p.getValue(), value, "value does not match – name: " + name);
+ assertEquals(p.getType(), type, "value does not match – name: " + name);
+ return;
+ }
+ }
+ fail("Named parameter not found: " + name);
+ }
+
+ @Test
+ public void testParseOptions_PrepareBatch() throws InvalidOptionsException, CLIParserException {
+ String[] args = new String[]{
+ Tokens.BATCH,
+ Tokens.SQL, SQL_1};
+ CLIOptions options = parseOptions(args);
+ options.validate();
+
+ assertEquals(options.getSql(), SQL_1);
+ assertEquals(options.getMode(), CLIOptions.MODE.PREPARE_BATCH);
+ }
+
+ @Test
+ public void testParseOptions_ExecuteBatch() throws InvalidOptionsException, CLIParserException {
+ String[] args = new String[]{
+ Tokens.BATCH,
+ Tokens.DB, DATABASE_NAME_1};
+ CLIOptions options = parseOptions(args);
+ options.validate();
+
+ assertEquals(options.getDatabaseName(), DATABASE_NAME_1);
+ assertEquals(options.getMode(), CLIOptions.MODE.EXECUTE_BATCH);
+ }
+
+ @Test
+ public void testParseOptions_ShowInfo_Help() throws InvalidOptionsException, CLIParserException {
+ String[] args = new String[]{Tokens.INFO_HELP};
+ CLIOptions options = parseOptions(args);
+ options.validate();
+
+ assertEquals(options.getMode(), CLIOptions.MODE.JUST_SHOW_INFO);
+ assertEquals(options.getShowInfo().size(), 1);
+ assertTrue(options.getShowInfo().contains(InfoType.HELP));
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java/sql-dk/src/test/java/info/globalcode/sql/dk/FunctionsTest.java Mon Mar 04 20:15:24 2019 +0100
@@ -0,0 +1,96 @@
+/**
+ * SQL-DK
+ * Copyright © 2013 František Kučera (frantovo.cz)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package info.globalcode.sql.dk;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import static org.testng.Assert.*;
+import org.testng.annotations.*;
+
+/**
+ *
+ * @author Ing. František Kučera (frantovo.cz)
+ */
+public class FunctionsTest {
+
+ @Test
+ public void testNotNull() {
+ Collection<String> c = null;
+ for (String s : Functions.notNull(c)) {
+ fail("Should not iterate through null collection");
+ }
+
+ c = new ArrayList<>();
+ c.add("ahoj");
+ int count = 0;
+ for (String s : Functions.notNull(c)) {
+ assertEquals(s, "ahoj", "Wrong item in collection");
+ count++;
+ }
+ assertEquals(count, 1, "Wrong number of iterations");
+ }
+
+ @Test
+ public void testLpad() {
+ String original = "abc";
+ String padded;
+
+ padded = Functions.lpad(original, 5);
+ assertEquals(padded, " abc");
+
+ padded = Functions.lpad(original, 2);
+ assertEquals(padded, original);
+ }
+
+ @Test
+ public void testRpad() {
+ String original = "abc";
+ String padded;
+
+ padded = Functions.rpad(original, 5);
+ assertEquals(padded, "abc ");
+
+ padded = Functions.rpad(original, 2);
+ assertEquals(padded, original);
+ }
+
+ @Test
+ public void testRepeat() {
+ assertEquals(Functions.repeat('f', 0), "");
+ assertEquals(Functions.repeat('f', 3), "fff");
+ }
+
+ @Test
+ public void testGetClassHierarchy() {
+ List<Class<? extends HierarchyMockClass2>> hierarchy = Functions.getClassHierarchy(HierarchyMockClass0.class, HierarchyMockClass2.class);
+ assertEquals(hierarchy.size(), 3, "invalid number of classes in the hierarchy");
+ assertEquals(hierarchy.get(0), HierarchyMockClass0.class);
+ assertEquals(hierarchy.get(1), HierarchyMockClass1.class);
+ assertEquals(hierarchy.get(2), HierarchyMockClass2.class);
+ }
+
+ private static class HierarchyMockClass0 extends HierarchyMockClass1 {
+ }
+
+ private static class HierarchyMockClass1 extends HierarchyMockClass2 {
+ }
+
+ private static class HierarchyMockClass2 {
+ }
+}
--- a/java/sql-dk/test/info/globalcode/sql/dk/CLIParserTest.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,195 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk;
-
-import info.globalcode.sql.dk.CLIParser.Tokens;
-import static info.globalcode.sql.dk.CLIParser.TYPE_NAME_SEPARATOR;
-import info.globalcode.sql.dk.InfoLister.InfoType;
-import java.io.ByteArrayInputStream;
-import java.util.Collection;
-import static org.testng.Assert.*;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-/**
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class CLIParserTest {
-
- private static final String DATABASE_NAME_1 = "some database 1";
- private static final String SQL_1 = "SELECT * FROM table1";
- private static final String DATA_1 = "aaa";
- private static final String DATA_2 = "bbb";
- private static final String DATA_3 = "ccc";
- private static final String NAME_1 = "param1";
- private static final String NAME_2 = "param2";
- private static final String NAME_3 = "param3";
- private CLIParser parser;
-
- @BeforeMethod
- public void setUpMethod() throws Exception {
- parser = new CLIParser();
- }
-
- private CLIOptions parseOptions(String[] args) throws CLIParserException {
- return parser.parseOptions(args, new ByteArrayInputStream("".getBytes()));
- }
-
- @Test
- public void testParseOptions_QueryNow_NoParams() throws InvalidOptionsException, CLIParserException {
- String[] args = new String[]{
- Tokens.DB, DATABASE_NAME_1,
- Tokens.SQL, SQL_1};
- CLIOptions options = parseOptions(args);
- options.validate();
-
- assertEquals(options.getDatabaseName(), DATABASE_NAME_1);
- assertEquals(options.getSql(), SQL_1);
- assertEquals(options.getMode(), CLIOptions.MODE.QUERY_NOW);
- assertTrue(options.getNamedParameters().isEmpty(), "Named parameters should be empty.");
- assertTrue(options.getNumberedParameters().isEmpty(), "Numbered parameters should be empty.");
- }
-
- @Test
- public void testParseOptions_QueryNow_Numbered() throws InvalidOptionsException, CLIParserException {
- String[] args = new String[]{
- Tokens.DB, DATABASE_NAME_1,
- Tokens.SQL, SQL_1,
- Tokens.DATA, DATA_1, DATA_2, DATA_3};
- CLIOptions options = parseOptions(args);
- options.validate();
-
- assertEquals(options.getDatabaseName(), DATABASE_NAME_1);
- assertEquals(options.getSql(), SQL_1);
- assertEquals(options.getMode(), CLIOptions.MODE.QUERY_NOW);
- assertEquals(options.getNumberedParameters().size(), 3);
- assertEquals(options.getNumberedParameters().get(0).getValue(), DATA_1);
- assertEquals(options.getNumberedParameters().get(1).getValue(), DATA_2);
- assertEquals(options.getNumberedParameters().get(2).getValue(), DATA_3);
- assertEquals(options.getNumberedParameters().get(0).getType(), Parameter.DEFAULT_TYPE);
- assertEquals(options.getNumberedParameters().get(1).getType(), Parameter.DEFAULT_TYPE);
- assertEquals(options.getNumberedParameters().get(2).getType(), Parameter.DEFAULT_TYPE);
- }
-
- @Test
- public void testParseOptions_QueryNow_Numbered_withTypes() throws InvalidOptionsException, CLIParserException {
- String[] args = new String[]{
- Tokens.DB, DATABASE_NAME_1,
- Tokens.SQL, SQL_1,
- Tokens.TYPES, " INTEGER,VARCHAR, BOOLEAN",
- Tokens.DATA, DATA_1, DATA_2, DATA_3};
- CLIOptions options = parseOptions(args);
- options.validate();
-
- assertEquals(options.getDatabaseName(), DATABASE_NAME_1);
- assertEquals(options.getSql(), SQL_1);
- assertEquals(options.getMode(), CLIOptions.MODE.QUERY_NOW);
- assertEquals(options.getNumberedParameters().size(), 3);
- assertEquals(options.getNumberedParameters().get(0).getValue(), DATA_1);
- assertEquals(options.getNumberedParameters().get(1).getValue(), DATA_2);
- assertEquals(options.getNumberedParameters().get(2).getValue(), DATA_3);
- assertEquals(options.getNumberedParameters().get(0).getType(), SQLType.INTEGER);
- assertEquals(options.getNumberedParameters().get(1).getType(), SQLType.VARCHAR);
- assertEquals(options.getNumberedParameters().get(2).getType(), SQLType.BOOLEAN);
- }
-
- @Test
- public void testParseOptions_QueryNow_Named() throws InvalidOptionsException, CLIParserException {
- String[] args = new String[]{
- Tokens.DB, DATABASE_NAME_1,
- Tokens.SQL, SQL_1,
- Tokens.DATA_NAMED, NAME_1, DATA_1, NAME_2, DATA_2, NAME_3, DATA_3};
- CLIOptions options = parseOptions(args);
- options.validate();
-
- assertEquals(options.getDatabaseName(), DATABASE_NAME_1);
- assertEquals(options.getSql(), SQL_1);
- assertEquals(options.getMode(), CLIOptions.MODE.QUERY_NOW);
- assertEquals(options.getNamedParameters().size(), 3);
- assertNamedParameter(options.getNamedParameters(), NAME_1, DATA_1, Parameter.DEFAULT_TYPE);
- assertNamedParameter(options.getNamedParameters(), NAME_2, DATA_2, Parameter.DEFAULT_TYPE);
- assertNamedParameter(options.getNamedParameters(), NAME_3, DATA_3, Parameter.DEFAULT_TYPE);
- }
-
- @Test
- public void testParseOptions_QueryNow_Named_withTypes() throws InvalidOptionsException, CLIParserException {
- String[] args = new String[]{
- Tokens.DB, DATABASE_NAME_1,
- Tokens.SQL, SQL_1,
- Tokens.NAME_PREFIX, "$",
- Tokens.TYPES, " " + NAME_1 + TYPE_NAME_SEPARATOR + "INTEGER" + "," + NAME_3 + TYPE_NAME_SEPARATOR + "BOOLEAN",
- Tokens.DATA_NAMED, NAME_1, DATA_1, NAME_2, DATA_2, NAME_3, DATA_3};
- CLIOptions options = parseOptions(args);
- options.validate();
-
- assertEquals(options.getDatabaseName(), DATABASE_NAME_1);
- assertEquals(options.getSql(), SQL_1);
- assertEquals(options.getMode(), CLIOptions.MODE.QUERY_NOW);
- assertEquals(options.getNamedParameters().size(), 3);
- assertNamedParameter(options.getNamedParameters(), NAME_1, DATA_1, SQLType.INTEGER);
- assertNamedParameter(options.getNamedParameters(), NAME_2, DATA_2, Parameter.DEFAULT_TYPE);
- assertNamedParameter(options.getNamedParameters(), NAME_3, DATA_3, SQLType.BOOLEAN);
- }
-
- private void assertNamedParameter(Collection<NamedParameter> params, String name, Object value, SQLType type) {
- for (NamedParameter p : params) {
- if (name.equals(p.getName())) {
- assertEquals(p.getValue(), value, "value does not match – name: " + name);
- assertEquals(p.getType(), type, "value does not match – name: " + name);
- return;
- }
- }
- fail("Named parameter not found: " + name);
- }
-
- @Test
- public void testParseOptions_PrepareBatch() throws InvalidOptionsException, CLIParserException {
- String[] args = new String[]{
- Tokens.BATCH,
- Tokens.SQL, SQL_1};
- CLIOptions options = parseOptions(args);
- options.validate();
-
- assertEquals(options.getSql(), SQL_1);
- assertEquals(options.getMode(), CLIOptions.MODE.PREPARE_BATCH);
- }
-
- @Test
- public void testParseOptions_ExecuteBatch() throws InvalidOptionsException, CLIParserException {
- String[] args = new String[]{
- Tokens.BATCH,
- Tokens.DB, DATABASE_NAME_1};
- CLIOptions options = parseOptions(args);
- options.validate();
-
- assertEquals(options.getDatabaseName(), DATABASE_NAME_1);
- assertEquals(options.getMode(), CLIOptions.MODE.EXECUTE_BATCH);
- }
-
- @Test
- public void testParseOptions_ShowInfo_Help() throws InvalidOptionsException, CLIParserException {
- String[] args = new String[]{Tokens.INFO_HELP};
- CLIOptions options = parseOptions(args);
- options.validate();
-
- assertEquals(options.getMode(), CLIOptions.MODE.JUST_SHOW_INFO);
- assertEquals(options.getShowInfo().size(), 1);
- assertTrue(options.getShowInfo().contains(InfoType.HELP));
- }
-}
\ No newline at end of file
--- a/java/sql-dk/test/info/globalcode/sql/dk/FunctionsTest.java Mon Mar 04 17:06:42 2019 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,96 +0,0 @@
-/**
- * SQL-DK
- * Copyright © 2013 František Kučera (frantovo.cz)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package info.globalcode.sql.dk;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import static org.testng.Assert.*;
-import org.testng.annotations.*;
-
-/**
- *
- * @author Ing. František Kučera (frantovo.cz)
- */
-public class FunctionsTest {
-
- @Test
- public void testNotNull() {
- Collection<String> c = null;
- for (String s : Functions.notNull(c)) {
- fail("Should not iterate through null collection");
- }
-
- c = new ArrayList<>();
- c.add("ahoj");
- int count = 0;
- for (String s : Functions.notNull(c)) {
- assertEquals(s, "ahoj", "Wrong item in collection");
- count++;
- }
- assertEquals(count, 1, "Wrong number of iterations");
- }
-
- @Test
- public void testLpad() {
- String original = "abc";
- String padded;
-
- padded = Functions.lpad(original, 5);
- assertEquals(padded, " abc");
-
- padded = Functions.lpad(original, 2);
- assertEquals(padded, original);
- }
-
- @Test
- public void testRpad() {
- String original = "abc";
- String padded;
-
- padded = Functions.rpad(original, 5);
- assertEquals(padded, "abc ");
-
- padded = Functions.rpad(original, 2);
- assertEquals(padded, original);
- }
-
- @Test
- public void testRepeat() {
- assertEquals(Functions.repeat('f', 0), "");
- assertEquals(Functions.repeat('f', 3), "fff");
- }
-
- @Test
- public void testGetClassHierarchy() {
- List<Class<? extends HierarchyMockClass2>> hierarchy = Functions.getClassHierarchy(HierarchyMockClass0.class, HierarchyMockClass2.class);
- assertEquals(hierarchy.size(), 3, "invalid number of classes in the hierarchy");
- assertEquals(hierarchy.get(0), HierarchyMockClass0.class);
- assertEquals(hierarchy.get(1), HierarchyMockClass1.class);
- assertEquals(hierarchy.get(2), HierarchyMockClass2.class);
- }
-
- private static class HierarchyMockClass0 extends HierarchyMockClass1 {
- }
-
- private static class HierarchyMockClass1 extends HierarchyMockClass2 {
- }
-
- private static class HierarchyMockClass2 {
- }
-}