2 * This file is part of yosql. It is subject to the license terms in the LICENSE file found in the top-level
3 * directory of this distribution and at https://creativecommons.org/publicdomain/zero/1.0/. No part of yosql,
4 * including this file, may be copied, modified, propagated, or distributed except according to the terms contained
7 package wtf
.metio
.yosql
.codegen
.files
;
9 import org
.slf4j
.cal10n
.LocLogger
;
10 import wtf
.metio
.yosql
.codegen
.lifecycle
.ApplicationErrors
;
11 import wtf
.metio
.yosql
.codegen
.lifecycle
.ParseLifecycle
;
12 import wtf
.metio
.yosql
.codegen
.orchestration
.ExecutionErrors
;
13 import wtf
.metio
.yosql
.models
.immutables
.FilesConfiguration
;
14 import wtf
.metio
.yosql
.models
.immutables
.SqlStatement
;
16 import java
.io
.BufferedReader
;
17 import java
.io
.IOException
;
18 import java
.io
.StringReader
;
19 import java
.nio
.file
.Files
;
20 import java
.nio
.file
.Path
;
21 import java
.util
.Objects
;
22 import java
.util
.concurrent
.atomic
.AtomicInteger
;
23 import java
.util
.function
.Consumer
;
24 import java
.util
.regex
.Pattern
;
25 import java
.util
.stream
.Stream
;
27 import static java
.util
.function
.Predicate
.not
;
30 * Default SQL file parser.
32 public final class DefaultSqlStatementParser
implements SqlStatementParser
{
34 private static final String SQL_COMMENT_PREFIX
= "--";
35 private static final String NEWLINE
= "\n";
37 private final Pattern statementSplitter
;
38 private final LocLogger logger
;
39 private final SqlConfigurationFactory factory
;
40 private final FilesConfiguration files
;
41 private final ExecutionErrors errors
;
43 public DefaultSqlStatementParser(
44 final LocLogger logger
,
45 final SqlConfigurationFactory factory
,
46 final FilesConfiguration files
,
47 final ExecutionErrors errors
) {
48 statementSplitter
= Pattern
.compile(files
.sqlStatementSeparator());
50 this.factory
= factory
;
56 public Stream
<SqlStatement
> parse(final Path source
) {
58 final var charset
= files
.sqlFilesCharset();
59 final var rawText
= Files
.readString(source
, charset
);
60 final var skippedText
= skipLines(rawText
);
61 final var counter
= new AtomicInteger(1);
62 return statementSplitter
.splitAsStream(skippedText
)
64 .filter(Objects
::nonNull
)
66 .filter(not(String
::isBlank
))
67 .map(statement
-> parseStatement(source
, statement
, counter
.getAndIncrement()));
68 } catch (final IOException exception
) {
69 errors
.add(exception
);
70 logger
.debug(ApplicationErrors
.FILE_PARSING_FAILED
, source
);
71 return Stream
.empty();
75 private String
skipLines(final String rawText
) {
76 final var builder
= new StringBuilder(rawText
);
77 for (int index
= 0; index
< files
.skipLines(); index
++) {
78 builder
.delete(0, builder
.indexOf("\n") + 1);
80 return builder
.toString();
83 private SqlStatement
parseStatement(
85 final String rawStatement
,
86 final int statementInFile
) {
87 final var yaml
= new StringBuilder();
88 final var sql
= new StringBuilder();
90 splitUpYamlAndSql(rawStatement
, yaml
::append
, sql
::append
);
92 logger
.trace(ParseLifecycle
.STATEMENT_PARSING_STARTING
, rawStatement
);
93 final String rawYaml
= yaml
.toString();
94 final String rawSqlStatement
= sql
.toString();
95 logger
.trace(ParseLifecycle
.STATEMENT_YAML_FRONT_MATTER_PARSED
, rawYaml
);
96 logger
.trace(ParseLifecycle
.STATEMENT_PARSED
, rawSqlStatement
);
98 final var parameterIndices
= SqlStatementParser
.extractParameterIndices(rawSqlStatement
);
99 final var configuration
= factory
.createConfiguration(source
, rawYaml
,
100 parameterIndices
, statementInFile
);
101 logger
.debug(ParseLifecycle
.STATEMENT_PARSING_FINISHED
, source
, configuration
.name());
102 return SqlStatement
.builder()
103 .setSourcePath(source
)
104 .setConfiguration(configuration
)
105 .setRawStatement(rawSqlStatement
)
109 private void splitUpYamlAndSql(
110 final String rawStatement
,
111 final Consumer
<String
> yaml
,
112 final Consumer
<String
> sql
) {
113 try (final var reader
= new StringReader(rawStatement
);
114 final var buffer
= new BufferedReader(reader
)) {
116 .filter(line
-> !line
.strip().isEmpty())
117 .filter(line
-> !SQL_COMMENT_PREFIX
.equals(line
.strip()))
118 .forEach(line
-> split(yaml
, sql
, line
));
119 } catch (final IOException exception
) {
120 errors
.add(exception
);
124 private static void split(
125 final Consumer
<String
> yaml
,
126 final Consumer
<String
> sql
,
128 if (line
.startsWith(SQL_COMMENT_PREFIX
)) {
129 yaml
.accept(line
.substring(2));
130 yaml
.accept(NEWLINE
);