This is an automated email from the git hooks/post-receive script. New commit to branch develop in repository nuiton-matrix. See https://gitlab.nuiton.org/nuiton/nuiton-matrix.git commit 12386cb2f8a9ab7acd32ef50e9ffd9107cdba984 Author: Benjamin POUSSIN <poussin@codelutin.com> Date: Thu Jan 19 18:47:49 2017 +0100 fixes #4139: add on MatrixFactory create with reader or File in argument --- .../org/nuiton/math/matrix/AbstractMatrixND.java | 152 +-------------- .../java/org/nuiton/math/matrix/MatrixFactory.java | 206 +++++++++++++++++++++ .../main/java/org/nuiton/math/matrix/MatrixND.java | 1 + .../nuiton/math/matrix/ImportExportMatrixTest.java | 3 +- 4 files changed, 213 insertions(+), 149 deletions(-) diff --git a/nuiton-matrix/src/main/java/org/nuiton/math/matrix/AbstractMatrixND.java b/nuiton-matrix/src/main/java/org/nuiton/math/matrix/AbstractMatrixND.java index c5e7bb9..6781db8 100644 --- a/nuiton-matrix/src/main/java/org/nuiton/math/matrix/AbstractMatrixND.java +++ b/nuiton-matrix/src/main/java/org/nuiton/math/matrix/AbstractMatrixND.java @@ -36,8 +36,6 @@ import java.util.List; import java.util.regex.Pattern; import org.apache.commons.collections.primitives.ArrayIntList; -import org.apache.commons.collections.primitives.IntList; -import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.math.NumberUtils; import org.apache.commons.logging.Log; @@ -87,8 +85,9 @@ public abstract class AbstractMatrixND implements MatrixND { // AbstractMatrixND */ public static final char CSV_SEPARATOR = ';'; - protected static final Pattern NUMBER = Pattern - .compile(" *[+-]?[0-9]*\\.?[0-9]+([eE][+-]?[0-9]+)? *"); + protected static final String NUMBER_REGEX = + " *[+-]?[0-9]*\\.?[0-9]+([eE][+-]?[0-9]+)? *"; + protected static final Pattern NUMBER = Pattern.compile(NUMBER_REGEX); protected void init(int[] dim) { this.dim = new int[dim.length]; @@ -1309,6 +1308,7 @@ public abstract class AbstractMatrixND implements MatrixND { // AbstractMatrixND * @param origin le point à partir duquel il faut faire l'importation * @throws IOException */ + @Override public void importCSV(File file, int[] origin) throws IOException { Reader reader = null; try { @@ -1339,154 +1339,12 @@ public abstract class AbstractMatrixND implements MatrixND { // AbstractMatrixND * @throws IOException */ protected void importCSVND(Reader reader, int[] origin) throws IOException { - - int c = -1; - StringBuffer number = new StringBuffer(20); - IntList coordinates = new ArrayIntList(); - - // read dimension - reader.read(); // skip [ - while ((c = reader.read()) != -1) { - if (c == ' ') { - // skip space - } else if (c == ',' || c == ']') { - if (NUMBER.matcher(number.toString()).matches()) { - int coord = Integer.parseInt(number.toString()); - coordinates.add(coord); - } - number.setLength(0); - - if (c == ']') { - break; - } - } else { - number.append((char) c); - } - } - int[] dimensions = coordinates.toArray(); - coordinates.clear(); - // / read dimension - - // read defaut value - while ((c = reader.read()) != -1) { - if (c == ',' || c == ' ' || c == '\r') { - // skip - } - else if (c == '\n') { - break; - } - else { - number.append((char) c); - } - } - double defaultValue = 0.0; - if (NUMBER.matcher(number.toString()).matches()) { - defaultValue = Double.parseDouble(number.toString()); - MatrixHelper.fill(this, defaultValue); - } - number.setLength(0); - // / read default value - - List[] semantics = new List[dimensions.length]; - for (int indexDim = 0 ; indexDim < dimensions.length ; indexDim++) { - List dimension = importCSVNDReadDimension(reader); - if (dimension != null && dimension.size() != dimensions[indexDim]) { - throw new MatrixException(String.format("Semantics %d count not equals to semantics dimension, excepted %d, got %d", - indexDim, dimensions[indexDim], dimension.size())); - } - semantics[indexDim] = dimension; - } - - if (ArrayUtils.contains(semantics, null)) { - throw new MatrixException("Wrong semantics definition : " + Arrays.toString(semantics)); - } - MatrixND matrix = MatrixFactory.getInstance().create(semantics); - MatrixHelper.fill(matrix, defaultValue); - do { - c = reader.read(); - if (c == ' ') { - // skip space - } else if (c == CSV_SEPARATOR) { - if (NUMBER.matcher(number.toString()).matches()) { - int coord = Integer.parseInt(number.toString()); - coordinates.add(coord); - } - number.setLength(0); - } else if (c == -1 || c == '\n' || c == '\r' || c == -1) { - // is line return or equivalent char because space is already - // skiped - // or end of stream - - // at end of line, we must see if the leave number* - Double val = null; - if (NUMBER.matcher(number.toString()).matches()) { - val = Double.valueOf(number.toString()); - } - number.setLength(0); - - if (!coordinates.isEmpty()) { - int[] coords = coordinates.toArray(); - matrix.setValue(coords, val); - coordinates.clear(); - } - } else { - number.append((char) c); - } - } while (c != -1); - + MatrixND matrix = MatrixFactory.getInstance().create(reader); // finally paste loaded matrix into this pasteSemantics(matrix); } /** - * Read a line and convert line to semantic value. - * - * Use: - * - mapper to convert semantics values - * - return null if line is empty - * - * @param reader reader to read - * @return semantics for readed line - * @throws IOException - */ - protected List importCSVNDReadDimension(Reader reader) throws IOException { - - StringBuffer buffer = new StringBuffer(); - int c = -1; - while ((c = reader.read()) != -1) { - if (c == '\n') { - break; - } - else { - buffer.append((char)c); - } - } - - // read must be in form: - // type : PK1, PK2, PK3... - List sems = null; - if (buffer.length() > 0) { - sems = new ArrayList(); - // get type - int twodIndex = buffer.indexOf(":"); - if (twodIndex == -1) { - throw new MatrixException("Can't parse semantics line as 'Type: ids'"); - } - String type = buffer.substring(0, twodIndex).trim(); - Class typeClass = MatrixFactory.getSemanticMapper().getType(type); - - // get semantics value - String semanticsStrings = buffer.substring(twodIndex +1).trim(); - String[] semanticsPKs = semanticsStrings.split("\\s*,\\s*"); - for (String semanticsPK : semanticsPKs) { - Object value = MatrixFactory.getSemanticMapper().getValue(typeClass, semanticsPK); - sems.add(value); - } - } - return sems; - } - - /** * Export dans un writer au format CSV de la matrice * * @param writer le writer ou copier la matrice diff --git a/nuiton-matrix/src/main/java/org/nuiton/math/matrix/MatrixFactory.java b/nuiton-matrix/src/main/java/org/nuiton/math/matrix/MatrixFactory.java index f79d30d..2680163 100644 --- a/nuiton-matrix/src/main/java/org/nuiton/math/matrix/MatrixFactory.java +++ b/nuiton-matrix/src/main/java/org/nuiton/math/matrix/MatrixFactory.java @@ -22,7 +22,17 @@ package org.nuiton.math.matrix; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.Reader; +import java.util.ArrayList; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.apache.commons.lang3.StringUtils; + /** * Cette classe permet de creer des matrices, toutes les creations de matrice @@ -39,6 +49,21 @@ import java.util.List; */ public class MatrixFactory { // MatrixFactory + + final static private Pattern DIM_AND_DEFAULT_LINE = Pattern.compile( + // match [1,2,3] + // match [1,2,3] 3.14 + "^\\s*\\[((?:\\s*[0-9]+\\s*,?)+)\\]\\s*,?\\s*(" + AbstractMatrixND.NUMBER_REGEX + ")?\\s*$"); + + final static private Pattern SEMANTICS_LINE = Pattern.compile( + // match: <Class name>:value1, value2, value3, ... + "^\\s*([^:]+)\\s*:\\s*(.+)\\s*$"); + + final static private Pattern DATA_LINE = Pattern.compile( + // match: x;y;z;...;<value> + "^(.*)" + AbstractMatrixND.CSV_SEPARATOR + "(.*)$" + ); + /** * If true, createVector return all time LazyVector to prevent memory * allocation when not necessary. LazyVector init real vector only when @@ -189,6 +214,187 @@ public class MatrixFactory { // MatrixFactory matrixFactoryThreadLocal.remove(); } + /** + * Create new Matrix from file. + * File contains data as describe for export/import CSV ND + * + * @param file + * @return + * @throws IOException + * @since 2.5.2 + */ + public MatrixND create(File file) throws IOException { + Reader in = new BufferedReader(new FileReader(file)); + try { + String matrixName = file.getName(); + int extPos = matrixName.lastIndexOf('.'); + if (extPos != -1) { // remove extension + matrixName = matrixName.substring(0, extPos); + } + + MatrixND result = create(in); + result.setName(matrixName); + return result; + } finally { + in.close(); + } + } + + /** + * Create new Matrix from file. + * File contains data as describe for export/import CSV ND + * @param reader + * @return + * @throws IOException + * @since 2.5.2 + */ + public MatrixND create(Reader reader) throws IOException { + BufferedReader in = new BufferedReader(reader); + + String line = readLine(in); + + if (line == null) { + throw new MatrixException("Bad file format, file is not Matrix"); + } + + // read metadata + int[] dimensions = readDimensions(line); + double defaultValue = readDefaultValue(line); + List[] semantics = readSemantics(dimensions, in); + + MatrixND matrix = this.create(semantics); + MatrixHelper.fill(matrix, defaultValue); + + // read data + line = readLine(in); + while (line != null) { + Matcher m = DATA_LINE.matcher(line); + if (m.matches()) { + int[] coords = readCoordinates(m.group(1)); + double val = readDouble(m.group(2)); + matrix.setValue(coords, val); + } else { + throw new MatrixException("Bad file format, can't read data"); + } + line = readLine(in); + } + + return matrix; + } + + private String readLine(BufferedReader in) throws IOException { + String line = in.readLine(); + while (line != null && StringUtils.isBlank(line)) { + line = in.readLine(); + } + return line; + } + + private int[] readDimensions(String line) throws IOException { + int[] dimensions; + + Matcher m = DIM_AND_DEFAULT_LINE.matcher(line); + + if (m.matches()) { + String dimString = m.group(1); + try { + String[] dimArrayString = dimString.split("\\s*,\\s*"); + dimensions = new int[dimArrayString.length]; + int i = 0; + for (String d : dimArrayString) { + dimensions[i++] = Integer.parseInt(d); + } + } catch (Exception eee) { + throw new MatrixException("Can't parse dimension value: " + dimString, eee); + } + } else { + throw new MatrixException("Line doesn't match dimension and default value information:" + line); + } + + return dimensions; + } + + private double readDefaultValue(String line) throws IOException { + double defaultValue = 0.0; + + Matcher m = DIM_AND_DEFAULT_LINE.matcher(line); + + if (m.matches()) { + String defaultValueString = m.group(2); + if (StringUtils.isNoneBlank(defaultValueString)) { + try { + defaultValue = Double.parseDouble(defaultValueString); + } catch (Exception eee) { + throw new MatrixException("Can't parse default value: " + defaultValueString, eee); + } + } + } else { + throw new MatrixException("Line doesn't match dimension and default value information:" + line); + } + return defaultValue; + } + + /** + * Read a line and convert line to semantic value. + * + * Use: + * - mapper to convert semantics values + */ + private List[] readSemantics(int[] dimensions, BufferedReader in) throws IOException { + List[] semantics = new List[dimensions.length]; + + for (int indexDim = 0 ; indexDim < dimensions.length ; indexDim++) { + String line = readLine(in); + if (line == null) { + throw new MatrixException("Bad file format, file is not Matrix (semantics missing)"); + } + Matcher m = SEMANTICS_LINE.matcher(line); + if (m.matches()) { + String type = m.group(1).trim(); + String[] semString = m.group(2).split("\\s*,\\s*"); + + List sems = new ArrayList(); + Class typeClass = MatrixFactory.getSemanticMapper().getType(type); + for (String s : semString) { + Object value = MatrixFactory.getSemanticMapper().getValue(typeClass, s); + sems.add(value); + } + if (sems.size() != dimensions[indexDim]) { + throw new MatrixException(String.format("Semantics %d count not equals to semantics dimension, excepted %d, got %d", + indexDim, dimensions[indexDim], sems.size())); + } + semantics[indexDim] = sems; + } else { + throw new MatrixException("Bad file format, line is not a semantics declaration: " + line); + } + } + + return semantics; + } + + private int[] readCoordinates(String s) { + String[] coords = s.split("\\s*" + AbstractMatrixND.CSV_SEPARATOR + "\\s*"); + int[] result = new int[coords.length]; + for (int i = 0, max = coords.length; i < max; i++) { + try { + result[i] = Integer.parseInt(coords[i]); + } catch (Exception eee) { + throw new MatrixException("Can't parse coordinate value: " + s, eee); + } + } + return result; + } + + private double readDouble(String s) { + try { + double result = Double.parseDouble(s); + return result; + } catch (Exception eee) { + throw new MatrixException("Can't parse value: " + s, eee); + } + } + + public MatrixND create(int[] dim) { return new MatrixNDImpl(this, dim); } diff --git a/nuiton-matrix/src/main/java/org/nuiton/math/matrix/MatrixND.java b/nuiton-matrix/src/main/java/org/nuiton/math/matrix/MatrixND.java index f73cc5f..e47ceca 100644 --- a/nuiton-matrix/src/main/java/org/nuiton/math/matrix/MatrixND.java +++ b/nuiton-matrix/src/main/java/org/nuiton/math/matrix/MatrixND.java @@ -940,6 +940,7 @@ public interface MatrixND extends Serializable, Cloneable { // MatrixND * @param writer le writer ou copier la matrice * @param withSemantics export ou pas des semantiques de la matrice dans le * writer + * @throws java.io.IOException */ public void exportCSVND(Writer writer, boolean withSemantics) throws IOException; diff --git a/nuiton-matrix/src/test/java/org/nuiton/math/matrix/ImportExportMatrixTest.java b/nuiton-matrix/src/test/java/org/nuiton/math/matrix/ImportExportMatrixTest.java index 3930b14..ef56a02 100644 --- a/nuiton-matrix/src/test/java/org/nuiton/math/matrix/ImportExportMatrixTest.java +++ b/nuiton-matrix/src/test/java/org/nuiton/math/matrix/ImportExportMatrixTest.java @@ -266,8 +266,7 @@ public class ImportExportMatrixTest { "1;1;0;4.0\n" + "1;1;1;42.0"; - MatrixND m = MatrixFactory.getInstance().create(new int[]{2, 2, 2}); - m.importCSV(new StringReader(matrix3D), null); + MatrixND m = MatrixFactory.getInstance().create(new StringReader(matrix3D)); // default value Assert.assertEquals(Math.PI, m.getValue(0, 1, 0), 0.00001); } -- To stop receiving notification emails like this one, please contact nuiton.org SCM administrator <admin+scm@nuiton.org>.