From d20a4009318fc8ecd624710a5f2b05270d890e72 Mon Sep 17 00:00:00 2001 From: vran Date: Tue, 28 Dec 2021 22:55:50 +0800 Subject: [PATCH] feat: init core module --- README.md | 80 +++++++- README/table-doc.png | Bin 0 -> 54241 bytes build.gradle | 42 ++++ core/build.gradle | 5 + .../core/doc/factory/DatabaseDocConfig.java | 49 +++++ .../core/doc/factory/DatabaseDocFactory.java | 11 ++ .../databasir/core/doc/factory/Sortable.java | 16 ++ .../doc/factory/TableColumnDocFactory.java | 12 ++ .../core/doc/factory/TableDocFactory.java | 12 ++ .../doc/factory/TableIndexDocFactory.java | 12 ++ .../doc/factory/TableTriggerDocFactory.java | 11 ++ .../mysql/MysqlTableTriggerDocFactory.java | 72 +++++++ .../doc/factory/extension/postgresql/.gitkeep | 0 .../factory/jdbc/JdbcDatabaseDocFactory.java | 45 +++++ .../jdbc/JdbcTableColumnDocFactory.java | 72 +++++++ .../doc/factory/jdbc/JdbcTableDocFactory.java | 59 ++++++ .../jdbc/JdbcTableIndexDocFactory.java | 64 ++++++ .../jdbc/JdbcTableTriggerDocFactory.java | 18 ++ .../databasir/core/doc/model/ColumnDoc.java | 26 +++ .../databasir/core/doc/model/DatabaseDoc.java | 28 +++ .../databasir/core/doc/model/IndexDoc.java | 21 ++ .../databasir/core/doc/model/TableDoc.java | 29 +++ .../databasir/core/doc/model/TriggerDoc.java | 28 +++ .../com/databasir/core/doc/render/Render.java | 16 ++ .../core/doc/render/RenderConfig.java | 75 +++++++ .../databasir/core/doc/render/html/.gitkeep | 0 .../doc/render/markdown/MarkdownBuilder.java | 108 ++++++++++ .../doc/render/markdown/MarkdownRender.java | 112 +++++++++++ .../databasir/core/doc/render/pdf/.gitkeep | 0 core/src/test/java/App.java | 43 ++++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 59536 bytes gradle/wrapper/gradle-wrapper.properties | 5 + gradlew | 185 ++++++++++++++++++ gradlew.bat | 89 +++++++++ settings.gradle | 3 + 35 files changed, 1347 insertions(+), 1 deletion(-) create mode 100644 README/table-doc.png create mode 100644 build.gradle create mode 100644 core/build.gradle create mode 100644 core/src/main/java/com/databasir/core/doc/factory/DatabaseDocConfig.java create mode 100644 core/src/main/java/com/databasir/core/doc/factory/DatabaseDocFactory.java create mode 100644 core/src/main/java/com/databasir/core/doc/factory/Sortable.java create mode 100644 core/src/main/java/com/databasir/core/doc/factory/TableColumnDocFactory.java create mode 100644 core/src/main/java/com/databasir/core/doc/factory/TableDocFactory.java create mode 100644 core/src/main/java/com/databasir/core/doc/factory/TableIndexDocFactory.java create mode 100644 core/src/main/java/com/databasir/core/doc/factory/TableTriggerDocFactory.java create mode 100644 core/src/main/java/com/databasir/core/doc/factory/extension/mysql/MysqlTableTriggerDocFactory.java create mode 100644 core/src/main/java/com/databasir/core/doc/factory/extension/postgresql/.gitkeep create mode 100644 core/src/main/java/com/databasir/core/doc/factory/jdbc/JdbcDatabaseDocFactory.java create mode 100644 core/src/main/java/com/databasir/core/doc/factory/jdbc/JdbcTableColumnDocFactory.java create mode 100644 core/src/main/java/com/databasir/core/doc/factory/jdbc/JdbcTableDocFactory.java create mode 100644 core/src/main/java/com/databasir/core/doc/factory/jdbc/JdbcTableIndexDocFactory.java create mode 100644 core/src/main/java/com/databasir/core/doc/factory/jdbc/JdbcTableTriggerDocFactory.java create mode 100644 core/src/main/java/com/databasir/core/doc/model/ColumnDoc.java create mode 100644 core/src/main/java/com/databasir/core/doc/model/DatabaseDoc.java create mode 100644 core/src/main/java/com/databasir/core/doc/model/IndexDoc.java create mode 100644 core/src/main/java/com/databasir/core/doc/model/TableDoc.java create mode 100644 core/src/main/java/com/databasir/core/doc/model/TriggerDoc.java create mode 100644 core/src/main/java/com/databasir/core/doc/render/Render.java create mode 100644 core/src/main/java/com/databasir/core/doc/render/RenderConfig.java create mode 100644 core/src/main/java/com/databasir/core/doc/render/html/.gitkeep create mode 100644 core/src/main/java/com/databasir/core/doc/render/markdown/MarkdownBuilder.java create mode 100644 core/src/main/java/com/databasir/core/doc/render/markdown/MarkdownRender.java create mode 100644 core/src/main/java/com/databasir/core/doc/render/pdf/.gitkeep create mode 100644 core/src/test/java/App.java create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat create mode 100644 settings.gradle diff --git a/README.md b/README.md index 67f115b..d1f2301 100644 --- a/README.md +++ b/README.md @@ -1 +1,79 @@ -# Databasir \ No newline at end of file +# Databasir + +Database document generator + +you could use `databasir` to generate database meta model, or render it as markdown / pdf (TODO) / html (TODO) + +# How to use + +## Database Meta to Java Model + +```java +java.sql.Connection connection = ...; +DatabaseDocConfig config = DatabaseDocConfig.builder() + .databaseName("Demo") + .connection(connection) + .build(); +DatabaseDoc doc = JdbcDatabaseDocFactory.of().create(config).orElseThrow(); +``` + +## Render as Markdown +```java +java.sql.Connection connection = ...; +DatabaseDocConfig config = DatabaseDocConfig.builder() + .databaseName("Demo") + .connection(connection) + .build(); +DatabaseDoc doc = JdbcDatabaseDocFactory.of().create(config).orElseThrow(); +try (FileOutputStream out = new FileOutputStream("doc.md")) { + RenderConfig renderConfig = new RenderConfig(); + Render.markdownRender(renderConfig).rendering(doc, out); +} catch (IOException e) { + throw new IllegalStateException(e); +} +``` + +- Example + +![](README/table-doc.png) + + +## Ignore tables or columns + +support regex pattern to ignore table or column + +```java +java.sql.Connection connection = ...; +DatabaseDocConfig config = DatabaseDocConfig.builder() + .databaseName("Demo") + .connection(connection) + .ignoreTableRegexes(Arrays.asList("mysql_*")) + .ignoreColumnRegexes(Arrays.asList("id")) + .build(); +DatabaseDoc doc = JdbcDatabaseDocFactory.of().create(config).orElseThrow(); +``` + +## Extension + +Default factory + +- tableDocFactory -> `com.databasir.core.doc.factory.jdbc.JdbcTableDocFactory` +- columnDocFactory -> `com.databasir.core.doc.factory.jdbc.JdbcTableColumnDocFactory` +- indexDocFactory -> `com.databasir.core.doc.factory.jdbc.JdbcTableIndexDocFactory` +- triggerDocFactory -> `com.databasir.core.doc.factory.jdbc.JdbcTableTriggerDocFactory` + +Custom configuration +```java +java.sql.Connection connection = ...; +DatabaseDocConfig config = DatabaseDocConfig.builder() + .databaseName("Demo") + .connection(connection) + .tableDocFactory(...) // your custom table doc factory + .tableColumnDocFactory(...) // your custom column doc factory + .tableIndexDocFactory(...) // your custom index doc factory + .tableTriggerDocFactory(...) // your custom trigger doc factory + .build(); +DatabaseDoc doc = JdbcDatabaseDocFactory.of().create(config).orElseThrow(); +``` + + diff --git a/README/table-doc.png b/README/table-doc.png new file mode 100644 index 0000000000000000000000000000000000000000..4631cbf4f2eb378ead40d0ca11c83b890bc7eafa GIT binary patch literal 54241 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 literal 0 HcmV?d00001 diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..74981ab --- /dev/null +++ b/build.gradle @@ -0,0 +1,42 @@ +plugins { + id 'java' +} + +subprojects { + + apply plugin: 'java' + + group 'cc.cc1234' + version '1.0-SNAPSHOT' + + repositories { + mavenCentral() + } + + ext { + lombokVersion = '1.18.22' + mapstructVersion = '1.4.2.Final' + junitVersion = '5.7.0' + slf4jVersion = '1.7.32' + } + + dependencies { + testImplementation "org.junit.jupiter:junit-jupiter-api:${junitVersion}" + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junitVersion}" + + implementation "org.projectlombok:lombok:${lombokVersion}" + annotationProcessor "org.projectlombok:lombok:${lombokVersion}" + + implementation "org.mapstruct:mapstruct:${mapstructVersion}" + annotationProcessor "org.mapstruct:mapstruct:${mapstructVersion}" + + implementation "org.slf4j:slf4j-api:${slf4jVersion}" + + } + + test { + useJUnitPlatform() + } +} + + diff --git a/core/build.gradle b/core/build.gradle new file mode 100644 index 0000000..9a94a40 --- /dev/null +++ b/core/build.gradle @@ -0,0 +1,5 @@ +dependencies { + implementation 'mysql:mysql-connector-java:8.0.27' + implementation 'commons-io:commons-io:2.11.0' + implementation 'org.commonmark:commonmark:0.18.1' +} diff --git a/core/src/main/java/com/databasir/core/doc/factory/DatabaseDocConfig.java b/core/src/main/java/com/databasir/core/doc/factory/DatabaseDocConfig.java new file mode 100644 index 0000000..3afb89f --- /dev/null +++ b/core/src/main/java/com/databasir/core/doc/factory/DatabaseDocConfig.java @@ -0,0 +1,49 @@ +package com.databasir.core.doc.factory; + +import com.databasir.core.doc.factory.jdbc.*; +import lombok.Builder; +import lombok.Getter; +import lombok.NonNull; + +import java.sql.Connection; +import java.util.Collections; +import java.util.List; +import java.util.regex.Pattern; + +@Builder +@Getter +public class DatabaseDocConfig { + + @NonNull + private String databaseName; + + @NonNull + private Connection connection; + + @Builder.Default + private List ignoreTableRegexes = Collections.emptyList(); + + @Builder.Default + private List ignoreColumnRegexes = Collections.emptyList(); + + @Builder.Default + private TableDocFactory tableDocFactory = new JdbcTableDocFactory(); + + @Builder.Default + private TableIndexDocFactory tableIndexDocFactory = new JdbcTableIndexDocFactory(); + + @Builder.Default + private TableTriggerDocFactory tableTriggerDocFactory = new JdbcTableTriggerDocFactory(); + + @Builder.Default + private TableColumnDocFactory tableColumnDocFactory = new JdbcTableColumnDocFactory(); + + public boolean tableIsIgnored(String tableName) { + return ignoreTableRegexes.stream().anyMatch(regex -> Pattern.matches(regex, tableName)); + } + + public boolean columnIsIgnored(String column) { + return ignoreColumnRegexes.stream().anyMatch(regex -> Pattern.matches(regex, column)); + } + +} diff --git a/core/src/main/java/com/databasir/core/doc/factory/DatabaseDocFactory.java b/core/src/main/java/com/databasir/core/doc/factory/DatabaseDocFactory.java new file mode 100644 index 0000000..de59447 --- /dev/null +++ b/core/src/main/java/com/databasir/core/doc/factory/DatabaseDocFactory.java @@ -0,0 +1,11 @@ +package com.databasir.core.doc.factory; + +import com.databasir.core.doc.model.DatabaseDoc; + +import java.util.Optional; + +public interface DatabaseDocFactory extends Sortable { + + Optional create(DatabaseDocConfig configuration); + +} diff --git a/core/src/main/java/com/databasir/core/doc/factory/Sortable.java b/core/src/main/java/com/databasir/core/doc/factory/Sortable.java new file mode 100644 index 0000000..4d8e4db --- /dev/null +++ b/core/src/main/java/com/databasir/core/doc/factory/Sortable.java @@ -0,0 +1,16 @@ +package com.databasir.core.doc.factory; + +public interface Sortable> extends Comparable { + + /** + * @return priority, min -> max means low -> high + */ + default int order() { + return Integer.MIN_VALUE; + } + + @Override + default int compareTo(T o) { + return Integer.compare(this.order(), o.order()); + } +} diff --git a/core/src/main/java/com/databasir/core/doc/factory/TableColumnDocFactory.java b/core/src/main/java/com/databasir/core/doc/factory/TableColumnDocFactory.java new file mode 100644 index 0000000..3401eb6 --- /dev/null +++ b/core/src/main/java/com/databasir/core/doc/factory/TableColumnDocFactory.java @@ -0,0 +1,12 @@ +package com.databasir.core.doc.factory; + +import com.databasir.core.doc.model.ColumnDoc; + +import java.sql.DatabaseMetaData; +import java.util.List; + +public interface TableColumnDocFactory extends Sortable { + + List create(String tableName, DatabaseMetaData metaData, DatabaseDocConfig configuration); + +} diff --git a/core/src/main/java/com/databasir/core/doc/factory/TableDocFactory.java b/core/src/main/java/com/databasir/core/doc/factory/TableDocFactory.java new file mode 100644 index 0000000..f4fb135 --- /dev/null +++ b/core/src/main/java/com/databasir/core/doc/factory/TableDocFactory.java @@ -0,0 +1,12 @@ +package com.databasir.core.doc.factory; + +import com.databasir.core.doc.model.TableDoc; + +import java.sql.DatabaseMetaData; +import java.util.List; + +public interface TableDocFactory extends Sortable { + + List create(DatabaseMetaData metaData, DatabaseDocConfig configuration); + +} diff --git a/core/src/main/java/com/databasir/core/doc/factory/TableIndexDocFactory.java b/core/src/main/java/com/databasir/core/doc/factory/TableIndexDocFactory.java new file mode 100644 index 0000000..68f7a1e --- /dev/null +++ b/core/src/main/java/com/databasir/core/doc/factory/TableIndexDocFactory.java @@ -0,0 +1,12 @@ +package com.databasir.core.doc.factory; + +import com.databasir.core.doc.model.IndexDoc; + +import java.sql.DatabaseMetaData; +import java.util.List; + +public interface TableIndexDocFactory extends Sortable { + + List create(String tableName, DatabaseMetaData metaData, DatabaseDocConfig configuration); + +} diff --git a/core/src/main/java/com/databasir/core/doc/factory/TableTriggerDocFactory.java b/core/src/main/java/com/databasir/core/doc/factory/TableTriggerDocFactory.java new file mode 100644 index 0000000..a0faaa9 --- /dev/null +++ b/core/src/main/java/com/databasir/core/doc/factory/TableTriggerDocFactory.java @@ -0,0 +1,11 @@ +package com.databasir.core.doc.factory; + +import com.databasir.core.doc.model.TriggerDoc; + +import java.sql.DatabaseMetaData; +import java.util.List; + +public interface TableTriggerDocFactory extends Sortable { + + List create(String tableName, DatabaseMetaData metaData, DatabaseDocConfig configuration); +} diff --git a/core/src/main/java/com/databasir/core/doc/factory/extension/mysql/MysqlTableTriggerDocFactory.java b/core/src/main/java/com/databasir/core/doc/factory/extension/mysql/MysqlTableTriggerDocFactory.java new file mode 100644 index 0000000..703724d --- /dev/null +++ b/core/src/main/java/com/databasir/core/doc/factory/extension/mysql/MysqlTableTriggerDocFactory.java @@ -0,0 +1,72 @@ +package com.databasir.core.doc.factory.extension.mysql; + +import com.databasir.core.doc.factory.DatabaseDocConfig; +import com.databasir.core.doc.factory.TableTriggerDocFactory; +import com.databasir.core.doc.model.TriggerDoc; +import lombok.extern.slf4j.Slf4j; + +import java.sql.DatabaseMetaData; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +@Slf4j +public class MysqlTableTriggerDocFactory implements TableTriggerDocFactory { + + @Override + public List create(String tableName, + DatabaseMetaData metaData, + DatabaseDocConfig configuration) { + String sql = "SELECT TRIGGER_CATALOG,\n" + + " TRIGGER_SCHEMA,\n" + + " TRIGGER_NAME,\n" + + " EVENT_MANIPULATION,\n" + + " EVENT_OBJECT_CATALOG,\n" + + " EVENT_OBJECT_SCHEMA,\n" + + " EVENT_OBJECT_TABLE,\n" + + " ACTION_ORDER,\n" + + " ACTION_CONDITION,\n" + + " ACTION_STATEMENT,\n" + + " ACTION_ORIENTATION,\n" + + " ACTION_TIMING,\n" + + " ACTION_REFERENCE_OLD_TABLE,\n" + + " ACTION_REFERENCE_NEW_TABLE,\n" + + " ACTION_REFERENCE_OLD_ROW,\n" + + " ACTION_REFERENCE_NEW_ROW,\n" + + " CREATED,\n" + + " SQL_MODE,\n" + + " DEFINER\n " + + "FROM information_schema.TRIGGERS WHERE EVENT_OBJECT_SCHEMA = ? AND EVENT_OBJECT_TABLE = ?"; + try { + PreparedStatement preparedStatement = configuration.getConnection() + .prepareStatement(sql); + preparedStatement.setObject(1, configuration.getDatabaseName()); + preparedStatement.setObject(2, tableName); + ResultSet results = preparedStatement.executeQuery(); + List triggers = new ArrayList<>(); + while (results.next()) { + String name = results.getString("TRIGGER_NAME"); + String statement = results.getString("ACTION_STATEMENT"); + String timing = results.getString("ACTION_TIMING"); + String manipulation = results.getString("EVENT_MANIPULATION"); + String created = results.getString("CREATED"); + TriggerDoc doc = TriggerDoc.builder() + .name(name) + .manipulation(manipulation) + .timing(timing) + .statement(statement) + .createAt(created) + .build(); + triggers.add(doc); + } + return triggers; + } catch (SQLException e) { + log.warn("create trigger doc failed", e); + return Collections.emptyList(); + } + } + +} diff --git a/core/src/main/java/com/databasir/core/doc/factory/extension/postgresql/.gitkeep b/core/src/main/java/com/databasir/core/doc/factory/extension/postgresql/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/core/src/main/java/com/databasir/core/doc/factory/jdbc/JdbcDatabaseDocFactory.java b/core/src/main/java/com/databasir/core/doc/factory/jdbc/JdbcDatabaseDocFactory.java new file mode 100644 index 0000000..2c5cb5a --- /dev/null +++ b/core/src/main/java/com/databasir/core/doc/factory/jdbc/JdbcDatabaseDocFactory.java @@ -0,0 +1,45 @@ +package com.databasir.core.doc.factory.jdbc; + +import com.databasir.core.doc.factory.DatabaseDocConfig; +import com.databasir.core.doc.factory.*; +import com.databasir.core.doc.model.DatabaseDoc; +import com.databasir.core.doc.model.TableDoc; + +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +public class JdbcDatabaseDocFactory implements DatabaseDocFactory { + + public static JdbcDatabaseDocFactory of() { + return new JdbcDatabaseDocFactory(); + } + + @Override + public Optional create(DatabaseDocConfig configuration) { + try { + DatabaseMetaData metaData = configuration.getConnection().getMetaData(); + ResultSet catalogs = metaData.getCatalogs(); + while (catalogs.next()) { + String catalogName = catalogs.getString("TABLE_CAT"); + if (Objects.equals(configuration.getDatabaseName(), catalogName)) { + TableDocFactory tableDocFactory = configuration.getTableDocFactory(); + List tableDocs = tableDocFactory.create(metaData, configuration); + DatabaseDoc databaseDoc = DatabaseDoc.builder() + .productName(metaData.getDatabaseProductName()) + .productVersion(metaData.getDatabaseProductVersion()) + .databaseName(catalogName) + .tables(tableDocs) + .build(); + return Optional.of(databaseDoc); + } + } + return Optional.empty(); + } catch (SQLException e) { + throw new IllegalStateException(e); + } + } +} diff --git a/core/src/main/java/com/databasir/core/doc/factory/jdbc/JdbcTableColumnDocFactory.java b/core/src/main/java/com/databasir/core/doc/factory/jdbc/JdbcTableColumnDocFactory.java new file mode 100644 index 0000000..bbdc3d8 --- /dev/null +++ b/core/src/main/java/com/databasir/core/doc/factory/jdbc/JdbcTableColumnDocFactory.java @@ -0,0 +1,72 @@ +package com.databasir.core.doc.factory.jdbc; + +import com.databasir.core.doc.factory.DatabaseDocConfig; +import com.databasir.core.doc.factory.TableColumnDocFactory; +import com.databasir.core.doc.model.ColumnDoc; +import lombok.extern.slf4j.Slf4j; + +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +@Slf4j +public class JdbcTableColumnDocFactory implements TableColumnDocFactory { + + @Override + public List create(String tableName, + DatabaseMetaData metaData, + DatabaseDocConfig configuration) { + try { + return doCreate(tableName, metaData, configuration); + } catch (SQLException e) { + throw new IllegalStateException(e); + } + } + + private List doCreate(String tableName, + DatabaseMetaData metaData, + DatabaseDocConfig configuration) throws SQLException { + List columnDocs = new ArrayList<>(); + String database = configuration.getDatabaseName(); + ResultSet columnsResult; + try { + columnsResult = metaData.getColumns(database, null, tableName, null); + } catch (SQLException e) { + log.warn("warn: ignore columns in " + database + "." + tableName); + return columnDocs; + } + while (columnsResult.next()) { + String columnName = columnsResult.getString("COLUMN_NAME"); + if (configuration.columnIsIgnored(columnName)) { + if (log.isWarnEnabled()) { + log.warn("ignore column: " + columnName); + } + } else { + String columnType = columnsResult.getString("TYPE_NAME"); + Integer columnSize = columnsResult.getInt("COLUMN_SIZE"); + Integer decimalDigits = columnsResult.getInt("DECIMAL_DIGITS"); + String defaultValue = columnsResult.getString("COLUMN_DEF"); + boolean isNullable = Objects.equals("YES", columnsResult.getString("IS_NULLABLE")); + boolean isAutoIncrement = Objects.equals("YES", columnsResult.getString("IS_AUTOINCREMENT")); + String columnComment = columnsResult.getString("REMARKS"); + ColumnDoc columnDoc = ColumnDoc.builder() + .name(columnName) + .type(columnType) + .size(columnSize) + .decimalDigits(decimalDigits) + .isNullable(isNullable) + .isAutoIncrement(isAutoIncrement) + .comment(columnComment) + .defaultValue(defaultValue) + .build(); + columnDocs.add(columnDoc); + } + + } + return columnDocs; + } + +} diff --git a/core/src/main/java/com/databasir/core/doc/factory/jdbc/JdbcTableDocFactory.java b/core/src/main/java/com/databasir/core/doc/factory/jdbc/JdbcTableDocFactory.java new file mode 100644 index 0000000..ce74f50 --- /dev/null +++ b/core/src/main/java/com/databasir/core/doc/factory/jdbc/JdbcTableDocFactory.java @@ -0,0 +1,59 @@ +package com.databasir.core.doc.factory.jdbc; + +import com.databasir.core.doc.factory.DatabaseDocConfig; +import com.databasir.core.doc.factory.*; +import com.databasir.core.doc.model.TableDoc; +import lombok.extern.slf4j.Slf4j; + +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +@Slf4j +public class JdbcTableDocFactory implements TableDocFactory { + + @Override + public List create(DatabaseMetaData metaData, + DatabaseDocConfig configuration) { + try { + return doCreateTableDoc(metaData, configuration); + } catch (SQLException e) { + throw new IllegalStateException(e); + } + } + + private List doCreateTableDoc(DatabaseMetaData metaData, + DatabaseDocConfig configuration) throws SQLException { + List tableDocs = new ArrayList<>(); + if (metaData == null) { + return tableDocs; + } + + String databaseName = configuration.getDatabaseName(); + ResultSet tablesResult = metaData.getTables(databaseName, null, null, null); + while (tablesResult.next()) { + String tableName = tablesResult.getString("TABLE_NAME"); + if (configuration.tableIsIgnored(tableName)) { + if (log.isWarnEnabled()) { + log.warn("ignore table: " + configuration.getDatabaseName() + "." + tableName); + } + } else { + String tableType = tablesResult.getString("TABLE_TYPE"); + String tableComment = tablesResult.getString("REMARKS"); + TableDoc tableDoc = TableDoc.builder() + .tableName(tableName) + .tableType(tableType) + .tableComment(tableComment) + .columns(configuration.getTableColumnDocFactory().create(tableName, metaData, configuration)) + .indexes(configuration.getTableIndexDocFactory().create(tableName, metaData, configuration)) + .triggers(configuration.getTableTriggerDocFactory().create(tableName, metaData, configuration)) + .build(); + tableDocs.add(tableDoc); + } + } + return tableDocs; + } + +} diff --git a/core/src/main/java/com/databasir/core/doc/factory/jdbc/JdbcTableIndexDocFactory.java b/core/src/main/java/com/databasir/core/doc/factory/jdbc/JdbcTableIndexDocFactory.java new file mode 100644 index 0000000..3c615d3 --- /dev/null +++ b/core/src/main/java/com/databasir/core/doc/factory/jdbc/JdbcTableIndexDocFactory.java @@ -0,0 +1,64 @@ +package com.databasir.core.doc.factory.jdbc; + +import com.databasir.core.doc.factory.DatabaseDocConfig; +import com.databasir.core.doc.factory.TableIndexDocFactory; +import com.databasir.core.doc.model.IndexDoc; +import lombok.extern.slf4j.Slf4j; + +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.*; + +@Slf4j +public class JdbcTableIndexDocFactory implements TableIndexDocFactory { + + @Override + public List create(String tableName, DatabaseMetaData metaData, DatabaseDocConfig configuration) { + try { + return doCreateIndexDocs(tableName, metaData, configuration); + } catch (SQLException e) { + throw new IllegalStateException(e); + } + } + + private List doCreateIndexDocs(String tableName, + DatabaseMetaData metaData, + DatabaseDocConfig configuration) + throws SQLException { + List indexDocs = new ArrayList<>(); + String databaseName = configuration.getDatabaseName(); + if (tableName == null || metaData == null) { + return indexDocs; + } + ResultSet indexResults; + try { + indexResults = metaData.getIndexInfo(databaseName, null, tableName, false, false); + } catch (SQLException e) { + log.warn("warn: ignore " + databaseName + "." + tableName); + return indexDocs; + } + + Map docsGroupByName = new HashMap<>(); + while (indexResults.next()) { + Boolean nonUnique = indexResults.getBoolean("NON_UNIQUE"); + String indexName = indexResults.getString("INDEX_NAME"); + String columnName = indexResults.getString("COLUMN_NAME"); + if (docsGroupByName.containsKey(indexName)) { + docsGroupByName.get(indexName).getColumnNames().add(columnName); + } else { + List columns = new ArrayList<>(); + columns.add(columnName); + IndexDoc columnDoc = IndexDoc.builder() + .indexName(indexName) + .columnNames(columns) + .isPrimaryKey(Objects.equals("PRIMARY", indexName)) + .isUniqueKey(Objects.equals(nonUnique, false)) + .build(); + docsGroupByName.put(indexName, columnDoc); + } + } + return new ArrayList<>(docsGroupByName.values()); + } + +} diff --git a/core/src/main/java/com/databasir/core/doc/factory/jdbc/JdbcTableTriggerDocFactory.java b/core/src/main/java/com/databasir/core/doc/factory/jdbc/JdbcTableTriggerDocFactory.java new file mode 100644 index 0000000..25ec97c --- /dev/null +++ b/core/src/main/java/com/databasir/core/doc/factory/jdbc/JdbcTableTriggerDocFactory.java @@ -0,0 +1,18 @@ +package com.databasir.core.doc.factory.jdbc; + +import com.databasir.core.doc.factory.DatabaseDocConfig; +import com.databasir.core.doc.factory.TableTriggerDocFactory; +import com.databasir.core.doc.model.TriggerDoc; + +import java.sql.DatabaseMetaData; +import java.util.Collections; +import java.util.List; + +public class JdbcTableTriggerDocFactory implements TableTriggerDocFactory { + + @Override + public List create(String tableName, DatabaseMetaData metaData, DatabaseDocConfig configuration) { + // note: jdbc not support get triggers + return Collections.emptyList(); + } +} diff --git a/core/src/main/java/com/databasir/core/doc/model/ColumnDoc.java b/core/src/main/java/com/databasir/core/doc/model/ColumnDoc.java new file mode 100644 index 0000000..96df982 --- /dev/null +++ b/core/src/main/java/com/databasir/core/doc/model/ColumnDoc.java @@ -0,0 +1,26 @@ +package com.databasir.core.doc.model; + +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +public class ColumnDoc { + + private String name; + + private String comment; + + private String type; + + private String defaultValue; + + private Integer size; + + private Integer decimalDigits; + + private Boolean isNullable; + + private Boolean isAutoIncrement; + +} \ No newline at end of file diff --git a/core/src/main/java/com/databasir/core/doc/model/DatabaseDoc.java b/core/src/main/java/com/databasir/core/doc/model/DatabaseDoc.java new file mode 100644 index 0000000..00a029b --- /dev/null +++ b/core/src/main/java/com/databasir/core/doc/model/DatabaseDoc.java @@ -0,0 +1,28 @@ +package com.databasir.core.doc.model; + +import lombok.Builder; +import lombok.Data; + +import java.util.Collections; +import java.util.List; + +@Data +@Builder +public class DatabaseDoc { + + private String productName; + + private String productVersion; + + private String driverName; + + private String driverVersion; + + private String databaseName; + + private String remark; + + @Builder.Default + private List tables = Collections.emptyList(); + +} diff --git a/core/src/main/java/com/databasir/core/doc/model/IndexDoc.java b/core/src/main/java/com/databasir/core/doc/model/IndexDoc.java new file mode 100644 index 0000000..5097234 --- /dev/null +++ b/core/src/main/java/com/databasir/core/doc/model/IndexDoc.java @@ -0,0 +1,21 @@ +package com.databasir.core.doc.model; + +import lombok.Builder; +import lombok.Data; + +import java.util.Collections; +import java.util.List; + +@Data +@Builder +public class IndexDoc { + + private String indexName; + + @Builder.Default + private List columnNames = Collections.emptyList(); + + private Boolean isPrimaryKey; + + private Boolean isUniqueKey; +} diff --git a/core/src/main/java/com/databasir/core/doc/model/TableDoc.java b/core/src/main/java/com/databasir/core/doc/model/TableDoc.java new file mode 100644 index 0000000..58c8617 --- /dev/null +++ b/core/src/main/java/com/databasir/core/doc/model/TableDoc.java @@ -0,0 +1,29 @@ +package com.databasir.core.doc.model; + +import lombok.Builder; +import lombok.Data; + +import java.util.Collections; +import java.util.List; + +@Data +@Builder +public class TableDoc { + + private String tableName; + + private String tableType; + + private String tableComment; + + @Builder.Default + private List columns = Collections.emptyList(); + + @Builder.Default + private List triggers = Collections.emptyList(); + + @Builder.Default + private List indexes = Collections.emptyList(); + + private String remark; +} diff --git a/core/src/main/java/com/databasir/core/doc/model/TriggerDoc.java b/core/src/main/java/com/databasir/core/doc/model/TriggerDoc.java new file mode 100644 index 0000000..32a64ae --- /dev/null +++ b/core/src/main/java/com/databasir/core/doc/model/TriggerDoc.java @@ -0,0 +1,28 @@ +package com.databasir.core.doc.model; + +import lombok.Builder; +import lombok.Data; + +/** + * now: only support mysql, postgresql. + */ +@Data +@Builder +public class TriggerDoc { + + private String name; + + /** + * example: BEFORE, AFTER + */ + private String timing; + + /** + * example: INSERT, UPDATE + */ + private String manipulation; + + private String statement; + + private String createAt; +} diff --git a/core/src/main/java/com/databasir/core/doc/render/Render.java b/core/src/main/java/com/databasir/core/doc/render/Render.java new file mode 100644 index 0000000..21a41ab --- /dev/null +++ b/core/src/main/java/com/databasir/core/doc/render/Render.java @@ -0,0 +1,16 @@ +package com.databasir.core.doc.render; + +import com.databasir.core.doc.model.DatabaseDoc; +import com.databasir.core.doc.render.markdown.MarkdownRender; + +import java.io.IOException; +import java.io.OutputStream; + +public interface Render { + + void rendering(DatabaseDoc doc, OutputStream outputStream) throws IOException; + + static Render markdownRender(RenderConfig configuration) { + return MarkdownRender.of(configuration); + } +} diff --git a/core/src/main/java/com/databasir/core/doc/render/RenderConfig.java b/core/src/main/java/com/databasir/core/doc/render/RenderConfig.java new file mode 100644 index 0000000..f39afab --- /dev/null +++ b/core/src/main/java/com/databasir/core/doc/render/RenderConfig.java @@ -0,0 +1,75 @@ +package com.databasir.core.doc.render; + +import com.databasir.core.doc.model.ColumnDoc; +import com.databasir.core.doc.model.IndexDoc; +import com.databasir.core.doc.model.TriggerDoc; +import lombok.Data; + +import java.util.LinkedHashMap; +import java.util.function.Function; + +@Data +public class RenderConfig { + + private Boolean renderTables = true; + + private Boolean renderColumns = true; + + private Boolean renderIndexes = true; + + private Boolean renderTriggers = true; + + private LinkedHashMap> columnTitleAndValueMapping = columnTitleAndValueMapping(); + + private LinkedHashMap> indexTitleAndValueMapping = indexTitleAndValueMapping(); + + private LinkedHashMap> triggerTitleAndValueMapping = triggerTitleAndValueMapping(); + + protected LinkedHashMap> columnTitleAndValueMapping() { + LinkedHashMap> mapping = new LinkedHashMap<>(); + mapping.put("Name", ColumnDoc::getName); + mapping.put("Type", column -> { + String type; + if (column.getDecimalDigits() == null || column.getDecimalDigits().equals(0)) { + type = column.getType() + + "(" + column.getSize().toString() + ")"; + } else { + type = column.getType() + + "(" + column.getSize().toString() + ", " + column.getDecimalDigits().toString() + ")"; + } + return type; + }); + mapping.put("Not Null", column -> column.getIsNullable() ? "" : "YES"); + mapping.put("Auto Increment", column -> column.getIsAutoIncrement() ? "YES" : ""); + mapping.put("Default", column -> { + if (column.getDefaultValue() == null) { + return ""; + } + if (column.getDefaultValue().trim().equals("")) { + return "'" + column.getDefaultValue() + "'"; + } + return column.getDefaultValue(); + }); + mapping.put("Comment", ColumnDoc::getComment); + return mapping; + } + + protected LinkedHashMap> indexTitleAndValueMapping() { + LinkedHashMap> mapping = new LinkedHashMap<>(); + mapping.put("Name", IndexDoc::getIndexName); + mapping.put("IsPrimary", index -> index.getIsPrimaryKey() ? "YES" : ""); + mapping.put("IsUnique", index -> index.getIsUniqueKey() ? "YES" : ""); + mapping.put("Columns", index -> String.join(", ", index.getColumnNames())); + return mapping; + } + + protected LinkedHashMap> triggerTitleAndValueMapping() { + LinkedHashMap> mapping = new LinkedHashMap<>(); + mapping.put("Name", TriggerDoc::getName); + mapping.put("Timing", trigger -> trigger.getTiming() + " " + trigger.getManipulation()); + mapping.put("Statement", trigger -> trigger.getStatement().replace("\n", " ") + .replace("\r", " ")); + mapping.put("Create At", TriggerDoc::getCreateAt); + return mapping; + } +} diff --git a/core/src/main/java/com/databasir/core/doc/render/html/.gitkeep b/core/src/main/java/com/databasir/core/doc/render/html/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/core/src/main/java/com/databasir/core/doc/render/markdown/MarkdownBuilder.java b/core/src/main/java/com/databasir/core/doc/render/markdown/MarkdownBuilder.java new file mode 100644 index 0000000..57cdb5b --- /dev/null +++ b/core/src/main/java/com/databasir/core/doc/render/markdown/MarkdownBuilder.java @@ -0,0 +1,108 @@ +package com.databasir.core.doc.render.markdown; + +import java.util.List; + +public class MarkdownBuilder { + + private static final String LINE = "\n"; + + private static final String DOUBLE_LINE = LINE + LINE; + + private StringBuilder builder = new StringBuilder(1024); + + private MarkdownBuilder() { + } + + public static MarkdownBuilder builder() { + return new MarkdownBuilder(); + } + + public MarkdownBuilder primaryTitle(String title) { + builder.append("# ").append(title).append(DOUBLE_LINE); + return this; + } + + public MarkdownBuilder secondTitle(String title) { + builder.append("## ").append(title).append(DOUBLE_LINE); + return this; + } + + public MarkdownBuilder thirdTitle(String title) { + builder.append("### ").append(title).append(DOUBLE_LINE); + return this; + } + + public MarkdownBuilder text(String text) { + builder.append(text).append(DOUBLE_LINE); + return this; + } + + public MarkdownBuilder table(List titles, List> rows) { + if (titles == null || titles.isEmpty()) { + throw new IllegalArgumentException("titles must not be null or empty"); + } + // build titles + builder.append("| "); + for (String title : titles) { + builder.append(title).append(" | "); + } + builder.append(LINE); + + // build separators + builder.append("| "); + for (String title : titles) { + builder.append("------").append(" | "); + } + builder.append(LINE); + + // build rows + for (List row : rows) { + builder.append("| "); + for (String column : row) { + builder.append(column).append(" | "); + } + builder.append(LINE); + } + builder.append(LINE); + return this; + } + + public MarkdownBuilder orderedList(List list) { + for (int i = 0; i < list.size(); i++) { + builder.append(i + 1).append(". ").append(list.get(i)).append(LINE); + } + builder.append(LINE); + return this; + } + + public MarkdownBuilder unorderedList(List list) { + for (String item : list) { + builder.append("- ").append(item).append(LINE); + } + builder.append(LINE); + return this; + } + + public MarkdownBuilder blockquotes(String content) { + builder.append("> ").append(content).append(DOUBLE_LINE); + return this; + } + + public MarkdownBuilder code(String languageType, String statement) { + builder.append("```").append(languageType).append(LINE) + .append(statement) + .append("```") + .append(DOUBLE_LINE); + return this; + } + + public MarkdownBuilder link(String text, String link) { + builder.append("[").append(text).append("]") + .append("(").append(link).append(")"); + return this; + } + + public String build() { + return builder.toString(); + } +} diff --git a/core/src/main/java/com/databasir/core/doc/render/markdown/MarkdownRender.java b/core/src/main/java/com/databasir/core/doc/render/markdown/MarkdownRender.java new file mode 100644 index 0000000..c62298d --- /dev/null +++ b/core/src/main/java/com/databasir/core/doc/render/markdown/MarkdownRender.java @@ -0,0 +1,112 @@ +package com.databasir.core.doc.render.markdown; + +import com.databasir.core.doc.model.DatabaseDoc; +import com.databasir.core.doc.model.TableDoc; +import com.databasir.core.doc.render.Render; +import com.databasir.core.doc.render.RenderConfig; +import lombok.Getter; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.stream.Collectors; + +public class MarkdownRender implements Render { + + @Getter + private final RenderConfig config; + + protected MarkdownRender(RenderConfig config) { + this.config = config; + } + + public static MarkdownRender of(RenderConfig config) { + return new MarkdownRender(config); + } + + @Override + public void rendering(DatabaseDoc doc, + OutputStream outputStream) throws IOException { + MarkdownBuilder contentBuilder = MarkdownBuilder.builder(); + contentBuilder.primaryTitle(doc.getDatabaseName()); + if (config.getRenderTables()) { + for (TableDoc table : doc.getTables()) { + buildTableName(contentBuilder, table); + if (config.getRenderColumns()) { + buildColumns(contentBuilder, table); + } + if (config.getRenderIndexes()) { + buildIndexes(contentBuilder, table); + } + if (config.getRenderTriggers()) { + buildTriggers(contentBuilder, table); + } + } + } + outputStream.write(contentBuilder.build().getBytes(StandardCharsets.UTF_8)); + } + + private void buildTableName(MarkdownBuilder contentBuilder, TableDoc table) { + String tableName; + if (table.getTableComment() == null || table.getTableComment().trim().isEmpty()) { + tableName = table.getTableName(); + } else { + tableName = table.getTableName() + "(" + table.getTableComment() + ")"; + } + contentBuilder.secondTitle(tableName); + } + + private void buildColumns(MarkdownBuilder contentBuilder, TableDoc table) { + contentBuilder.unorderedList(Collections.singletonList("columns")); + List> allColumnRows = table.getColumns() + .stream() + .map(column -> config.getColumnTitleAndValueMapping() + .values() + .stream() + .map(mapping -> mapping.apply(column)) + .collect(Collectors.toList())) + .collect(Collectors.toList()); + contentBuilder.table(tableTitles(), allColumnRows); + } + + private void buildIndexes(MarkdownBuilder contentBuilder, TableDoc table) { + contentBuilder.unorderedList(Collections.singletonList("indexes")); + List> allIndexRows = table.getIndexes().stream() + .map(index -> config.getIndexTitleAndValueMapping() + .values() + .stream() + .map(mapping -> mapping.apply(index)) + .collect(Collectors.toList())) + .collect(Collectors.toList()); + contentBuilder.table(indexTitles(), allIndexRows); + } + + private void buildTriggers(MarkdownBuilder contentBuilder, TableDoc table) { + if (table.getTriggers() == null || table.getTriggers().isEmpty()) { + return; + } + + contentBuilder.unorderedList(Collections.singletonList("triggers")); + List> allRows = table.getTriggers().stream() + .map(trigger -> config.getTriggerTitleAndValueMapping() + .values() + .stream() + .map(mapping -> mapping.apply(trigger)) + .collect(Collectors.toList())) + .collect(Collectors.toList()); + contentBuilder.table(triggerTitles(), allRows); + } + + private List tableTitles() { + return new ArrayList<>(config.getColumnTitleAndValueMapping().keySet()); + } + + private List indexTitles() { + return new ArrayList<>(config.getIndexTitleAndValueMapping().keySet()); + } + + private List triggerTitles() { + return new ArrayList<>(config.getTriggerTitleAndValueMapping().keySet()); + } +} diff --git a/core/src/main/java/com/databasir/core/doc/render/pdf/.gitkeep b/core/src/main/java/com/databasir/core/doc/render/pdf/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/core/src/test/java/App.java b/core/src/test/java/App.java new file mode 100644 index 0000000..7d6b01d --- /dev/null +++ b/core/src/test/java/App.java @@ -0,0 +1,43 @@ +import com.databasir.core.doc.factory.DatabaseDocConfig; +import com.databasir.core.doc.factory.extension.mysql.MysqlTableTriggerDocFactory; +import com.databasir.core.doc.factory.jdbc.JdbcDatabaseDocFactory; +import com.databasir.core.doc.model.DatabaseDoc; +import com.databasir.core.doc.render.Render; +import com.databasir.core.doc.render.RenderConfig; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.Properties; + +public class App { + public static void main(String[] args) throws SQLException, ClassNotFoundException { + // get database connection + Class.forName("com.mysql.cj.jdbc.Driver"); + Properties info = new Properties(); + info.put("user", "root"); + info.put("password", "123456"); + // this config is used by mysql + info.put("useInformationSchema", "true"); + String url = "jdbc:mysql://localhost:3306/patient?useUnicode=true&characterEncoding=UTF-8&useSSL=false&allowPublicKeyRetrieval=true"; + var connection = DriverManager.getConnection(url, info); + + // generate doc model + var config = DatabaseDocConfig.builder() + .databaseName("user") + .connection(connection) + .tableTriggerDocFactory(new MysqlTableTriggerDocFactory()) + .build(); + DatabaseDoc doc = JdbcDatabaseDocFactory.of().create(config).orElseThrow(); + + // render as markdown + try (FileOutputStream out = new FileOutputStream("user.md")) { + RenderConfig renderConfig = new RenderConfig(); + renderConfig.setRenderTriggers(true); + Render.markdownRender(renderConfig).rendering(doc, out); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..7454180f2ae8848c63b8b4dea2cb829da983f2fa GIT binary patch literal 59536 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 QuEJmfE|#`Dqx|px0L@K;Y5)KL literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..69a9715 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..744e882 --- /dev/null +++ b/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MSYS* | MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..230e893 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,3 @@ +rootProject.name = 'databasir' +include 'core' +