/* Copyright 2009-2015 David Hadka * * This file is part of the MOEA Framework. * * The MOEA Framework is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * The MOEA Framework 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 Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the MOEA Framework. If not, see <http://www.gnu.org/licenses/>. */ package org.moeaframework.analysis.sensitivity; import java.io.Closeable; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.Reader; import java.util.Iterator; import java.util.NoSuchElementException; import org.moeaframework.core.FrameworkException; import org.moeaframework.util.io.CommentedLineReader; /** * Reader of files containing matrices. A matrix contains numerical data * separated into rows and columns. The values in a row are separated by * whitespace. * <p> * The file can contain commented lines starting with '#' characters. * <p> * Parsing stops at the first error. Check the {@code error} flag to determine * if an error was encountered. An exception may or may not be thrown, * depending on the type of error and the value of the * {@code suppressExceptions} flag. If exceptions are suppressed, a warning * message will be printed. */ class MatrixReader implements Iterable<double[]>, Iterator<double[]>, Closeable { /** * The underlying reader. */ private final CommentedLineReader reader; /** * The expected number of columns; or {@code -1} if the matrix has no * fixed column count. */ private final int numberOfColumns; /** * The next row to be returned; or {@code null} if the next row has * not yet been read. */ private double[] nextRow; /** * {@code true} if an error occurred parsing the file; {@code false} * otherwise. */ private boolean error; /** * {@code true} if errors are suppressed; {@code false} otherwise. I/O * errors will still be thrown. */ private boolean supressExceptions; /** * Constructs a reader for loading a matrix contained in the specified file. * * @param file the file containing the matrix * @throws FileNotFoundException if the file was not found */ public MatrixReader(File file) throws FileNotFoundException { this(new FileReader(file), -1); } /** * Constructs a reader for loading a matrix contained in the specified file. * * @param file the file containing the matrix * @param numberOfColumns the expected number of columns; or {@code -1} if * the matrix has no fixed column count * @throws FileNotFoundException if the file was not found */ public MatrixReader(File file, int numberOfColumns) throws FileNotFoundException { this(new FileReader(file), numberOfColumns); } /** * Constructs a reader for loading a matrix accessed through the underlying * reader. * * @param reader the underlying reader */ public MatrixReader(Reader reader) { this(reader, -1); } /** * Constructs a reader for loading a matrix accessed through the underlying * reader. * * @param reader the underlying reader * @param numberOfColumns the expected number of columns; or {@code -1} if * the matrix has no fixed column count */ public MatrixReader(Reader reader, int numberOfColumns) { super(); if (reader instanceof CommentedLineReader) { this.reader = (CommentedLineReader)reader; } else { this.reader = new CommentedLineReader(reader); } this.numberOfColumns = numberOfColumns; } @Override public Iterator<double[]> iterator() { return this; } @Override public boolean hasNext() { try { if (error) { return false; } if (nextRow == null) { nextRow = readNextRow(); } return nextRow != null; } catch (IOException e) { throw new FrameworkException(e); } } @Override public double[] next() { if (!hasNext()) { throw new NoSuchElementException(); } double[] result = nextRow; nextRow = null; return result; } /** * Returns the next row from the matrix; or {@code null} if an end-of-file * or a malformed line was reached. * * @return the next row from the matrix; or {@code null} if an end-of-file * or a malformed line was reached * @throws IOException if an I/O error occurred */ private double[] readNextRow() throws IOException { String line = reader.readLine(); if (line == null) { return null; } String[] tokens = line.trim().split("\\s+"); if ((numberOfColumns >= 0) && (tokens.length != numberOfColumns)) { error = true; if (supressExceptions) { System.err.println("insufficient number of entries in row, ignoring remaining rows in the file"); return null; } else { throw new IOException("insufficient number of entries in row"); } } double[] entry = new double[tokens.length]; try { for (int i = 0; i < tokens.length; i++) { entry[i] = Double.parseDouble(tokens[i]); } } catch (NumberFormatException e) { error = true; if (supressExceptions) { return null; } else { throw new IOException("invalid entry in row", e); } } return entry; } @Override public void remove() { throw new UnsupportedOperationException(); } @Override public void close() throws IOException { reader.close(); } /** * Returns {@code true} if exceptions are suppressed; {@code false} * otherwise. Low-level I/O exceptions will still be thrown. * * @return {@code true} if exceptions are suppressed; {@code false} * otherwise */ boolean isSupressExceptions() { return supressExceptions; } /** * Set to {@code true} to suppress exceptions; {@code false} otherwise. * Low-level I/O exceptions will still be thrown. * * @param supressExceptions {@code true} if exceptions are suppressed; * {@code false} otherwise */ void setSupressExceptions(boolean supressExceptions) { this.supressExceptions = supressExceptions; } }