All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.nuiton.topia.persistence.script.SqlScriptReader Maven / Gradle / Ivy

The newest version!
package org.nuiton.topia.persistence.script;

/*-
 * #%L
 * ObServe Toolkit :: ToPIA Extension
 * %%
 * Copyright (C) 2017 - 2018 IRD, Ultreia.io
 * %%
 * 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
 * .
 * #L%
 */

import io.ultreia.java4all.util.SingletonSupplier;
import org.apache.commons.lang3.mutable.MutableLong;
import org.nuiton.topia.persistence.TopiaException;
import org.nuiton.util.GZUtil;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.function.Supplier;
import java.util.zip.GZIPInputStream;

/**
 * Create an iterable on a SQL text content. The content is iterated on each SQL
 * statement. For information the class handles semi-colon in quote.
 * 

* File example: * INSERT INTO client (prenom, age) VALUES ('John', 11); * INSERT INTO client (prenom, age) VALUES ('Jack', 12); *

* Then: * SqlScriptReader source = new SqlScriptReader(stream); * for (String sql : source) { * // process sql variable * } * Created by tchemit on 10/05/2018. * * @author Tony Chemit - [email protected] * @author jruchaud */ @SuppressWarnings("WeakerAccess") public class SqlScriptReader implements Iterable, Closeable { private final SingletonSupplier source; private final boolean skipComment; private final boolean skipTrim; private final MutableLong statementCount = new MutableLong(); private final boolean gzip; private SqlScriptReader(SingletonSupplier source, boolean skipComment, boolean skipTrim, boolean gzip) { this.source = source; this.skipComment = skipComment; this.skipTrim = skipTrim; this.gzip = gzip; } public static SqlScriptReader of(String source) { return builder(Objects.requireNonNull(source)).build(); } public static SqlScriptReader of(byte[] source) { return builder(Objects.requireNonNull(source)).build(); } public static SqlScriptReader of(Path source) { return builder(Objects.requireNonNull(source)).build(); } public static SqlScriptReader of(URL source) { return builder(Objects.requireNonNull(source)).build(); } public static SqlScriptReader of(InputStream source) { return builder(source).build(); } public static Builder builder(String source) { return builder(Objects.requireNonNull(source).getBytes()); } public static Builder builder(InputStream content) { return new Builder(() -> new BufferedInputStream(Objects.requireNonNull(content))); } public static Builder builder(byte[] source) { return new Builder(() -> new BufferedInputStream(new ByteArrayInputStream(Objects.requireNonNull(source)))); } public static Builder builder(Path source) { return new Builder(() -> { try { return new BufferedInputStream(Files.newInputStream(Objects.requireNonNull(source))); } catch (IOException e) { throw new IllegalArgumentException("Can't get input stream from source: " + source, e); } }); } public static Builder builder(URL source) { return new Builder(() -> { try { return new BufferedInputStream(Objects.requireNonNull(source).openStream()); } catch (IOException e) { throw new IllegalArgumentException("Can't get input stream from source: " + source, e); } }); } public static Builder builder(SqlScriptReader location) { return new Builder(location.getSource(), location.isGzip()); } @SuppressWarnings("NullableProblems") @Override public Iterator iterator() { return skipComment || skipTrim ? new SqlFileReaderIteratorWithoutComment(source, statementCount, skipComment, skipTrim) : new SqlFileReaderIterator(source, statementCount); } public long getStatementCount() { return statementCount.getValue(); } public boolean isGzip() { return gzip; } public SingletonSupplier getSource() { return source; } @Override public void close() throws IOException { if (source.withValue()) { source.get().close(); } } enum SqlFileParserState {NORMAL, QUOTE, COMMENT} public static class Builder { private final boolean gzip; private final SingletonSupplier sourceReader; private final Supplier source; private Charset encoding = StandardCharsets.UTF_8; private boolean skipComment = true; private boolean skipTrim = true; public Builder(Supplier source) { this.source = Objects.requireNonNull(source); this.sourceReader = null; try { this.gzip = GZUtil.isGzipStream(source.get()); } catch (IOException e) { throw new IllegalArgumentException("Can't determine if input stream is gzip", e); } } public Builder(SingletonSupplier source, boolean gzip) { this.source = null; this.sourceReader = Objects.requireNonNull(source); this.gzip = gzip; } public Builder keepCommentLine() { skipComment = false; return this; } public Builder keepEmptyLine() { skipTrim = false; return this; } public Builder encoding(Charset encoding) { this.encoding = Objects.requireNonNull(encoding); return this; } public SqlScriptReader build() { if (source != null) { InputStream stream = source.get(); try { if (gzip) { stream = new GZIPInputStream(this.source.get()); } } catch (IOException e) { throw new IllegalStateException("Can't get gzip input stream", e); } InputStream finalStream = stream; return new SqlScriptReader(SingletonSupplier.of(() -> new BufferedReader(new InputStreamReader(finalStream, encoding))), skipComment, skipTrim, gzip); } return new SqlScriptReader(sourceReader, skipComment, skipTrim, gzip); } } /** * Use to create an iterator on the iterable. */ public static class SqlFileReaderIterator implements Iterator { private final SingletonSupplier source; private final StringBuilder buffer; private final MutableLong statementCount; /** The variable is used to keep if the iterator reach the end */ private int scanner; private SqlFileReaderIterator(SingletonSupplier source, MutableLong statementCount) { this.source = source; this.statementCount = statementCount; this.buffer = new StringBuilder(); } @Override public boolean hasNext() { return scanner != -1; } @Override public String next() { if (!hasNext()) { throw new NoSuchElementException(); } SqlFileParserState state = SqlFileParserState.NORMAL; buffer.setLength(0); try { while ((scanner = source.get().read()) != -1) { char character = (char) scanner; switch (state) { case NORMAL: switch (character) { // Remove useless character case '\n': case '\r': break; // Search the end of query case ';': buffer.append(";"); return returnStatement(); // Enter comment state if you have -- case '-': int length = buffer.length(); if (length != 0 && buffer.charAt(length - 1) == '-') { state = SqlFileParserState.COMMENT; } buffer.append(character); break; // Enter quote state case '\'': state = SqlFileParserState.QUOTE; buffer.append(character); break; // By default append character default: buffer.append(character); break; } break; case QUOTE: // Remove useless character switch (character) { // Search the end of quote case '\'': state = SqlFileParserState.NORMAL; buffer.append(character); break; // By default append character default: buffer.append(character); break; } break; case COMMENT: switch (character) { // Search the end of comment case '\n': case '\r': return returnStatement(); // By default append character default: buffer.append(character); break; } break; } } } catch (IOException ex) { throw new TopiaException(ex); } return returnStatement(); } private String returnStatement() { statementCount.increment(); return buffer.toString(); } @Override public void remove() { throw new UnsupportedOperationException("remove"); } } public static class SqlFileReaderIteratorWithoutComment implements Iterator { private final SqlFileReaderIterator delegate; private final MutableLong statementCount; private final boolean skipComment; private final boolean skipTrim; private String next; private SqlFileReaderIteratorWithoutComment(SingletonSupplier source, MutableLong statementCount, boolean skipComment, boolean skipTrim) { this.delegate = new SqlFileReaderIterator(source, new MutableLong()); this.statementCount = statementCount; this.skipComment = skipComment; this.skipTrim = skipTrim; } @Override public boolean hasNext() { if (next != null) { return true; } boolean hasNext = delegate.hasNext(); if (hasNext) { next = delegate.next(); if (skipTrim && next.trim().isEmpty()) { next = null; return hasNext(); } if (skipComment && next.trim().startsWith("--")) { next = null; return hasNext(); } } return hasNext; } @Override public String next() { if (!hasNext()) { throw new NoSuchElementException(); } statementCount.increment(); String next = this.next; this.next = null; return next; } @Override public void remove() { throw new UnsupportedOperationException("remove"); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy