/* Copyright (c) 2001-2019, The HSQL Development Group
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the HSQL Development Group nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.hsqldb.cmdline.sqltool;
import java.net.URL;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.io.InputStreamReader;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
This class does not impement or extend java.io classes/interfaces, and is
not a Reader in the sense of java.io.Reader.
It is a 'reader' in the sense that it provides a method that reads and
return records from a text file.
The unique use case here is that records are delimited with a regular
expression pattern yet the file contents are read incrementally to minimize
RAM usage.
/**
* This class does not impement or extend java.io classes/interfaces, and is
* not a Reader in the sense of java.io.Reader.
* It is a 'reader' in the sense that it provides a method that reads and
* return records from a text file.
*
* The unique use case here is that records are delimited with a regular
* expression pattern yet the file contents are read incrementally to minimize
* RAM usage.
*/
public class FileRecordReader {
/*
* We purposefully use a non-buffered Reader. We are doing buffering
* outselves.
* We have to read linearly from the front, so combined with previous
* requirement, that narrows us to FileReader or FileInputStream.
* Since we need to get at characters, FileReader is the most direct
* tool for us to use.
*/
// Can lower dramatically, all the way to 1, to test buffering.
public static final int INITIAL_CHARBUFFER_SIZE = 10240;
private URL url;
private InputStreamReader reader;
private Pattern recordPattern;
private long postRead;
private StringBuilder stringBuffer = new StringBuilder();
private char[] charBuffer = new char[INITIAL_CHARBUFFER_SIZE];
Legacy constructor.
See following constructor for documentation.
/**
* Legacy constructor.
* See following constructor for documentation.
*/
public FileRecordReader(
String filePath, String recordDelimiterRegex, String encoding)
throws IOException, UnsupportedEncodingException {
this(new URL("file", null, filePath),
recordDelimiterRegex, encoding);
}
Throws: - PatternSyntaxException –
- UnsupportedEncodingException –
/**
* @throws java.util.regex.PatternSyntaxException
* @throws UnsupportedEncodingException
*/
public FileRecordReader(
URL inUrl, String recordDelimiterRegex, String encoding)
throws IOException, UnsupportedEncodingException {
url = inUrl;
reader = new InputStreamReader(url.openStream(), encoding);
recordPattern = Pattern.compile(
"(.*?)(" + recordDelimiterRegex + ").*", Pattern.DOTALL);
}
Free up resources and close all potentially open I/O streams.
/**
* Free up resources and close all potentially open I/O streams.
*/
public void close() throws IOException {
if (reader == null)
throw new IllegalStateException("File already closed: " + url);
reader.close();
reader = null;
}
public String getName() {
String tableName = url.getPath();
int i;
i = tableName.lastIndexOf('/');
if (i > 0) tableName = tableName.substring(i+1);
i = tableName.lastIndexOf('\\');
if (i > 0) tableName = tableName.substring(i+1);
return tableName;
}
public String toString() {
return url.toString();
}
public boolean isOpen() {
return reader != null;
}
To be replaced by proper unit test class
Throws: - IOException –
/**
* To be replaced by proper unit test class
*
* @throws IOException
*/
public static void main(String[] sa) throws IOException {
if (sa.length != 2)
throw new IllegalArgumentException(
"SYNTAX: java " + FileRecordReader.class.getName()
+ " file.txt RECORD_DELIM");
FileRecordReader frr = new FileRecordReader(sa[0], sa[1], "UTF-8");
int i = 0;
String r;
while ((r = frr.nextRecord()) != null)
System.out.println("Rec #" + (++i) + ": [" + r + ']');
}
Throws: Returns: null if no more records in input file
/**
* @return null if no more records in input file
* @throws IOException
*/
public String nextRecord() throws IOException {
Matcher matcher;
boolean reloaded = false;
while (true) {
matcher = recordPattern.matcher(stringBuffer);
if (matcher.matches()) {
String rec = matcher.group(1);
stringBuffer.delete(0, matcher.end(2));
//System.err.println(" REM=(" + stringBuffer + ')');
return rec;
}
if (reader == null) {
if (stringBuffer.length() < 1) return null;
String rec = stringBuffer.toString();
stringBuffer.setLength(0);
//System.err.println(" Rem=()");
return rec;
}
reload(reloaded);
//System.err.println(" Reloaded to {" + stringBuffer + '}');
reloaded = true;
}
}
Throws: @param increaseBuffer. If true, grab 2 x as many bytes as previous read.
/**
* @param increaseBuffer. If true, grab 2 x as many bytes as previous read.
* @throws IOException
*/
private void reload(boolean increaseBuffer) throws IOException {
if (reader == null)
throw new IllegalStateException(
"Attempt to reload after source file has been closed");
if (increaseBuffer) charBuffer = new char[charBuffer.length * 2];
//if (increaseBuffer) System.err.println("-> " + charBuffer.length);
int retVal = reader.read(charBuffer);
// Indicate OED for 0, since we could get into loop by returning 0:
if (retVal > 0)
stringBuffer.append(charBuffer, 0, retVal);
else
close();
}
}