/*
* Copyright 2010 Red Hat, Inc. and/or its affiliates.
*
* 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
*
* http://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.
*/
package org.optaplanner.examples.common.persistence;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.io.FilenameUtils;
import org.optaplanner.core.api.domain.solution.PlanningSolution;
/**
* @param <Solution_> the solution type, the class with the {@link PlanningSolution} annotation
*/
public abstract class AbstractTxtSolutionImporter<Solution_> extends AbstractSolutionImporter<Solution_> {
private static final String DEFAULT_INPUT_FILE_SUFFIX = "txt";
protected AbstractTxtSolutionImporter(SolutionDao<Solution_> solutionDao) {
super(solutionDao);
}
protected AbstractTxtSolutionImporter(boolean withoutDao) {
super(withoutDao);
}
@Override
public String getInputFileSuffix() {
return DEFAULT_INPUT_FILE_SUFFIX;
}
public abstract TxtInputBuilder<Solution_> createTxtInputBuilder();
@Override
public Solution_ readSolution(File inputFile) {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(inputFile), "UTF-8"))) {
TxtInputBuilder<Solution_> txtInputBuilder = createTxtInputBuilder();
txtInputBuilder.setInputFile(inputFile);
txtInputBuilder.setBufferedReader(reader);
try {
Solution_ solution = txtInputBuilder.readSolution();
logger.info("Imported: {}", inputFile);
return solution;
} catch (IllegalArgumentException | IllegalStateException e) {
throw new IllegalArgumentException("Exception in inputFile (" + inputFile + ")", e);
}
} catch (IOException e) {
throw new IllegalArgumentException("Could not read the file (" + inputFile.getName() + ").", e);
}
}
public Solution_ readSolution(URL inputURL) {
try (BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(inputURL.openStream(), "UTF-8"))) {
TxtInputBuilder<Solution_> txtInputBuilder = createTxtInputBuilder();
txtInputBuilder.setInputFile(new File(inputURL.getFile()));
txtInputBuilder.setBufferedReader(bufferedReader);
try {
return txtInputBuilder.readSolution();
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Exception in inputURL (" + inputURL + ")", e);
} catch (IllegalStateException e) {
throw new IllegalStateException("Exception in inputURL (" + inputURL + ")", e);
}
} catch (IOException e) {
throw new IllegalArgumentException("Could not read the inputURL (" + inputURL + ").", e);
}
}
public static abstract class TxtInputBuilder<Solution_> extends InputBuilder {
protected File inputFile;
protected BufferedReader bufferedReader;
public void setInputFile(File inputFile) {
this.inputFile = inputFile;
}
public void setBufferedReader(BufferedReader bufferedReader) {
this.bufferedReader = bufferedReader;
}
public abstract Solution_ readSolution() throws IOException;
// ************************************************************************
// Helper methods
// ************************************************************************
public String getInputId() {
return FilenameUtils.getBaseName(inputFile.getPath());
}
// ************************************************************************
// Read methods
// ************************************************************************
public void readEmptyLine() throws IOException {
readConstantLine("");
}
public void readConstantLine(String constantRegex) throws IOException {
String line = bufferedReader.readLine();
if (line == null) {
throw new IllegalArgumentException("File ends before a line is expected to be a constant regex ("
+ constantRegex + ").");
}
String value = line.trim();
if (!value.matches(constantRegex)) {
throw new IllegalArgumentException("Read line (" + line + ") is expected to be a constant regex ("
+ constantRegex + ").");
}
}
public boolean readOptionalConstantLine(String constantRegex) throws IOException {
bufferedReader.mark(1024);
boolean valid = true;
String line = bufferedReader.readLine();
if (line == null) {
valid = false;
} else {
String value = line.trim();
if (!value.matches(constantRegex)) {
valid = false;
}
}
if (!valid) {
bufferedReader.reset();
}
return valid;
}
public void skipOptionalConstantLines(String constantRegex) throws IOException {
boolean valid = true;
while (valid) {
valid = readOptionalConstantLine(constantRegex);
}
}
public void readUntilConstantLine(String constantRegex) throws IOException {
String line;
String value;
do {
line = bufferedReader.readLine();
if (line == null) {
throw new IllegalArgumentException("File ends before a line is expected to be a constant regex ("
+ constantRegex + ").");
}
value = line.trim();
} while (!value.matches(constantRegex));
}
public int readIntegerValue() throws IOException {
return readIntegerValue("");
}
public int readIntegerValue(String prefixRegex) throws IOException {
return readIntegerValue(prefixRegex, "");
}
public int readIntegerValue(String prefixRegex, String suffixRegex) throws IOException {
String line = bufferedReader.readLine();
if (line == null) {
throw new IllegalArgumentException("File ends before a line is expected to contain an integer value ("
+ prefixRegex + "<value>" + suffixRegex + ").");
}
String value = removePrefixSuffixFromLine(line, prefixRegex, suffixRegex);
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Read line (" + line + ") is expected to contain an integer value ("
+ value + ").", e);
}
}
public long readLongValue() throws IOException {
return readLongValue("");
}
public long readLongValue(String prefixRegex) throws IOException {
return readLongValue(prefixRegex, "");
}
public long readLongValue(String prefixRegex, String suffixRegex) throws IOException {
String line = bufferedReader.readLine();
if (line == null) {
throw new IllegalArgumentException("File ends before a line is expected to contain an integer value ("
+ prefixRegex + "<value>" + suffixRegex + ").");
}
String value = removePrefixSuffixFromLine(line, prefixRegex, suffixRegex);
try {
return Long.parseLong(value);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Read line (" + line + ") is expected to contain an integer value ("
+ value + ").", e);
}
}
public String readStringValue() throws IOException {
return readStringValue("");
}
public String readStringValue(String prefixRegex) throws IOException {
return readStringValue(prefixRegex, "");
}
public String readStringValue(String prefixRegex, String suffixRegex) throws IOException {
String line = bufferedReader.readLine();
if (line == null) {
throw new IllegalArgumentException("File ends before a line is expected to contain an string value ("
+ prefixRegex + "<value>" + suffixRegex + ").");
}
return removePrefixSuffixFromLine(line, prefixRegex, suffixRegex);
}
public String readOptionalStringValue(String defaultValue) throws IOException {
return readOptionalStringValue("", defaultValue);
}
public String readOptionalStringValue(String prefixRegex, String defaultValue) throws IOException {
return readOptionalStringValue(prefixRegex, "", defaultValue);
}
public String readOptionalStringValue(String prefixRegex, String suffixRegex, String defaultValue) throws IOException {
bufferedReader.mark(1024);
boolean valid = true;
String value = bufferedReader.readLine();
if (value == null) {
valid = false;
} else {
value = value.trim();
if (value.matches("^" + prefixRegex + ".*")) {
value = value.replaceAll("^" + prefixRegex + "(.*)", "$1");
} else {
valid = false;
}
if (value.matches(".*" + suffixRegex + "$")) {
value = value.replaceAll("(.*)" + suffixRegex + "$", "$1");
} else {
valid = false;
}
}
if (!valid) {
bufferedReader.reset();
return defaultValue;
}
value = value.trim();
return value;
}
public String removePrefixSuffixFromLine(String line, String prefixRegex, String suffixRegex) {
String value = line.trim();
if (!value.matches("^" + prefixRegex + ".*")) {
throw new IllegalArgumentException("Read line (" + line + ") is expected to start with prefixRegex ("
+ prefixRegex + ").");
}
value = value.replaceAll("^" + prefixRegex + "(.*)", "$1");
if (!value.matches(".*" + suffixRegex + "$")) {
throw new IllegalArgumentException("Read line (" + line + ") is expected to end with suffixRegex ("
+ suffixRegex + ").");
}
value = value.replaceAll("(.*)" + suffixRegex + "$", "$1");
value = value.trim();
return value;
}
// ************************************************************************
// Split methods
// ************************************************************************
public String[] splitBySpace(String line) {
return splitBySpace(line, null);
}
public String[] splitBySpace(String line, Integer numberOfTokens) {
return splitBy(line, "\\ ", "a space ( )", numberOfTokens, false, false);
}
public String[] splitBySpace(String line, Integer minimumNumberOfTokens, Integer maximumNumberOfTokens) {
return splitBy(line, "\\ ", "a space ( )", minimumNumberOfTokens, maximumNumberOfTokens, false, false);
}
public String[] splitBySpace(String line, Integer minimumNumberOfTokens, Integer maximumNumberOfTokens,
boolean trim, boolean removeQuotes) {
return splitBy(line, "\\ ", "a space ( )", minimumNumberOfTokens, maximumNumberOfTokens, trim, removeQuotes);
}
public String[] splitBySpacesOrTabs(String line) {
return splitBySpacesOrTabs(line, null);
}
public String[] splitBySpacesOrTabs(String line, Integer numberOfTokens) {
return splitBy(line, "[\\ \\t]+", "spaces or tabs", numberOfTokens, false, false);
}
public String[] splitBySpacesOrTabs(String line, Integer minimumNumberOfTokens, Integer maximumNumberOfTokens) {
return splitBy(line, "[\\ \\t]+", "spaces or tabs", minimumNumberOfTokens, maximumNumberOfTokens,
false, false);
}
public String[] splitByPipelineAndTrim(String line, int numberOfTokens) {
return splitBy(line, "\\|", "a pipeline (|)", numberOfTokens, true, false);
}
public String[] splitBySemicolonSeparatedValue(String line) {
return splitBy(line, ";", "a semicolon (;)", null, false, true);
}
public String[] splitBySemicolonSeparatedValue(String line, int numberOfTokens) {
return splitBy(line, ";", "a semicolon (;)", numberOfTokens, false, true);
}
public String[] splitByCommaAndTrim(String line, int numberOfTokens) {
return splitBy(line, "\\,", "a comma (,)", numberOfTokens, true, false);
}
public String[] splitByCommaAndTrim(String line, Integer minimumNumberOfTokens, Integer maximumNumberOfTokens) {
return splitBy(line, "\\,", "a comma (,)", minimumNumberOfTokens, maximumNumberOfTokens, true, false);
}
public String[] splitBy(String line, String delimiterRegex, String delimiterName,
Integer numberOfTokens, boolean trim, boolean removeQuotes) {
return splitBy(line, delimiterRegex, delimiterName, numberOfTokens, numberOfTokens, trim, removeQuotes);
}
public String[] splitBy(String line, String delimiterRegex, String delimiterName,
Integer minimumNumberOfTokens, Integer maximumNumberOfTokens, boolean trim, boolean removeQuotes) {
String[] lineTokens = line.split(delimiterRegex);
if (removeQuotes) {
List<String> lineTokenList = new ArrayList<>(lineTokens.length);
for (int i = 0; i < lineTokens.length; i++) {
String token = lineTokens[i];
while ((trim ? token.trim() : token).startsWith("\"") && !(trim ? token.trim() : token).endsWith("\"")) {
i++;
if (i >= lineTokens.length) {
throw new IllegalArgumentException("The line (" + line
+ ") has an invalid use of quotes (\").");
}
String delimiter;
switch (delimiterRegex) {
case "\\ ":
delimiter = " ";
break;
case "\\,":
delimiter = ",";
break;
default:
throw new IllegalArgumentException("Not supported delimiterRegex (" + delimiterRegex + ")");
}
token += delimiter + lineTokens[i];
}
if (trim) {
token = token.trim();
}
if (token.startsWith("\"") && token.endsWith("\"")) {
token = token.substring(1, token.length() - 1);
token = token.replaceAll("\"\"", "\"");
}
lineTokenList.add(token);
}
lineTokens = lineTokenList.toArray(new String[0]);
}
if (minimumNumberOfTokens != null && lineTokens.length < minimumNumberOfTokens) {
throw new IllegalArgumentException("Read line (" + line + ") has " + lineTokens.length
+ " tokens but is expected to contain at least " + minimumNumberOfTokens
+ " tokens separated by " + delimiterName + ".");
}
if (maximumNumberOfTokens != null && lineTokens.length > maximumNumberOfTokens) {
throw new IllegalArgumentException("Read line (" + line + ") has " + lineTokens.length
+ " tokens but is expected to contain at most " + maximumNumberOfTokens
+ " tokens separated by " + delimiterName + ".");
}
if (trim) {
for (int i = 0; i < lineTokens.length; i++) {
lineTokens[i] = lineTokens[i].trim();
}
}
return lineTokens;
}
public boolean parseBooleanFromNumber(String token) {
switch (token) {
case "0":
return false;
case "1":
return true;
default:
throw new IllegalArgumentException("The token (" + token
+ ") is expected to be 0 or 1 representing a boolean.");
}
}
}
}