You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2013/10/04 21:08:19 UTC
svn commit: r1529262 [2/3] - in /sis/trunk: ./
application/sis-console/src/main/java/org/apache/sis/console/
core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/gml/
core/sis-metadata/src/test/java/org/apache/sis/xml/
core/sis-referencing/src/...
Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Solver.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Solver.java?rev=1529262&r1=1529261&r2=1529262&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Solver.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Solver.java [UTF-8] Fri Oct 4 19:08:18 2013
@@ -17,6 +17,8 @@
package org.apache.sis.referencing.operation.matrix;
import org.opengis.referencing.operation.Matrix;
+import org.apache.sis.internal.util.DoubleDouble;
+import org.apache.sis.util.resources.Errors;
import org.apache.sis.util.ArraysExt;
@@ -96,68 +98,121 @@ final class Solver implements Matrix {
}
/**
- * Computes the inverse of the given matrix. This method shall be invoked only for square matrices
- * (this is <strong>not</strong> verified by this method).
+ * Computes the inverse of the given matrix. This method shall be invoked only for square matrices.
+ *
+ * @throws NoninvertibleMatrixException If the {@code X} matrix is not square or singular.
*/
static MatrixSIS inverse(final MatrixSIS X) throws NoninvertibleMatrixException {
- return solve(X, IDENTITY, X.getNumRow());
+ final int size = X.getNumRow();
+ final int numCol = X.getNumCol();
+ if (numCol != size) {
+ throw new NoninvertibleMatrixException(Errors.format(Errors.Keys.NonInvertibleMatrix_2, size, numCol));
+ }
+ return solve(X, IDENTITY, null, size, size);
}
/**
* Solves {@code X} × <var>U</var> = {@code Y}.
* This method is an adaptation of the {@code LUDecomposition} class of the JAMA matrix package.
*
+ * @param X The matrix to invert.
+ * @param Y The desired result of {@code X} × <var>U</var>.
+ * @throws NoninvertibleMatrixException If the {@code X} matrix is not square or singular.
+ */
+ static MatrixSIS solve(final MatrixSIS X, final Matrix Y) throws NoninvertibleMatrixException {
+ final int size = X.getNumRow();
+ final int numCol = X.getNumCol();
+ if (numCol != size) {
+ throw new NoninvertibleMatrixException(Errors.format(Errors.Keys.NonInvertibleMatrix_2, size, numCol));
+ }
+ final int innerSize = Y.getNumCol();
+ GeneralMatrix.ensureNumRowMatch(size, Y, innerSize);
+ double[] eltY = null;
+ if (Y instanceof GeneralMatrix) {
+ eltY = ((GeneralMatrix) Y).elements;
+ if (eltY.length == size * innerSize) {
+ eltY = null; // Matrix does not contains error terms.
+ }
+ }
+ return solve(X, Y, eltY, size, innerSize);
+ }
+
+ /**
+ * Implementation of {@code solve} and {@code inverse} methods.
+ * Use a "left-looking", dot-product, Crout/Doolittle algorithm.
+ *
* <p>This method does <strong>not</strong> checks the matrix size.
* Check for matrix size shall be performed by the caller like below:</p>
*
* {@preformat java
* final int size = X.getNumRow();
* if (X.getNumCol() != size) {
- * throw new NoninvertibleTransformException("Matrix must be square.");
+ * throw new NoninvertibleMatrixException("Matrix must be square.");
* }
* if (Y.getNumRow() != size) {
* throw new MismatchedMatrixSizeException("Matrix row dimensions must agree.");
* }
* }
*
- * @param X The matrix to invert.
+ * @param X The matrix to invert.
+ * @param Y The desired result of {@code X} × <var>U</var>.
+ * @param eltY Elements and error terms of the {@code Y} matrix, or {@code null} if not available.
+ * @param size The value of {@code X.getNumRow()}, {@code X.getNumCol()} and {@code Y.getNumRow()}.
* @param innerSize The value of {@code Y.getNumCol()}.
- * @throws NoninvertibleMatrixException If the {@code X} matrix is singular.
+ * @throws NoninvertibleMatrixException If the {@code X} matrix is not square or singular.
*/
- static MatrixSIS solve(final MatrixSIS X, final Matrix Y, final int innerSize)
- throws NoninvertibleMatrixException
+ private static MatrixSIS solve(final MatrixSIS X, final Matrix Y, final double[] eltY,
+ final int size, final int innerSize) throws NoninvertibleMatrixException
{
- final int size = X.getNumRow();
- /*
- * Use a "left-looking", dot-product, Crout/Doolittle algorithm.
- */
- final double[] LU = X.getElements();
+ assert (X.getNumRow() == size && X.getNumCol() == size) : size;
+ assert (Y.getNumRow() == size && Y.getNumCol() == innerSize) || (Y instanceof Solver);
+
+ final int errorLU = size * size;
+ final double[] LU = GeneralMatrix.getExtendedElements(X, size, size, true);
+ assert errorLU == GeneralMatrix.indexOfErrors(size, size, LU);
final int[] pivot = new int[size];
for (int j=0; j<size; j++) {
pivot[j] = j;
}
- final double[] column = new double[size];
+ final double[] column = new double[size * 2]; // [0 … size-1] : column values; [size … 2*size-1] : error terms.
+ final DoubleDouble acc = new DoubleDouble(); // Temporary variable for sum ("accumulator") and subtraction.
+ final DoubleDouble rat = new DoubleDouble(); // Temporary variable for products and ratios.
for (int i=0; i<size; i++) {
/*
* Make a copy of the i-th column.
*/
for (int j=0; j<size; j++) {
- column[j] = LU[j*size + i];
+ final int k = j*size + i;
+ column[j] = LU[k]; // Value
+ column[j + size] = LU[k + errorLU]; // Error
}
/*
- * Apply previous transformations.
+ * Apply previous transformations. This part is equivalent to the following code,
+ * but using double-double arithmetic instead than the primitive 'double' type:
+ *
+ * double sum = 0;
+ * for (int k=0; k<kmax; k++) {
+ * sum += LU[rowOffset + k] * column[k];
+ * }
+ * LU[rowOffset + i] = (column[j] -= sum);
*/
for (int j=0; j<size; j++) {
final int rowOffset = j*size;
final int kmax = Math.min(j,i);
- double s = 0.0;
+ acc.clear();
for (int k=0; k<kmax; k++) {
- s += LU[rowOffset + k] * column[k];
+ rat.setFrom(LU, rowOffset + k, errorLU);
+ rat.multiply(column, k, size);
+ acc.add(rat);
}
- LU[rowOffset + i] = (column[j] -= s);
+ acc.subtract(column, j, size);
+ acc.negate();
+ acc.storeTo(column, j, size);
+ acc.storeTo(LU, rowOffset + i, errorLU);
}
/*
- * Find pivot and exchange if necessary.
+ * Find pivot and exchange if necessary. There is no floating-point arithmetic here
+ * (ignoring the comparison for magnitude order), only work on index values.
*/
int p = i;
for (int j=i; ++j < size;) {
@@ -169,17 +224,28 @@ final class Solver implements Matrix {
final int pRow = p*size;
final int iRow = i*size;
for (int k=0; k<size; k++) { // Swap two full rows.
- ArraysExt.swap(LU, pRow + k, iRow + k);
+ DoubleDouble.swap(LU, pRow + k, iRow + k, errorLU);
}
ArraysExt.swap(pivot, p, i);
}
/*
- * Compute multipliers.
+ * Compute multipliers. This part is equivalent to the following code, but
+ * using double-double arithmetic instead than the primitive 'double' type:
+ *
+ * final double sum = LU[i*size + i];
+ * if (sum != 0.0) {
+ * for (int j=i; ++j < size;) {
+ * LU[j*size + i] /= sum;
+ * }
+ * }
*/
- final double d = LU[i*size + i];
- if (d != 0.0) {
+ acc.setFrom(LU, i*size + i, errorLU);
+ if (!acc.isZero()) {
for (int j=i; ++j < size;) {
- LU[j*size + i] /= d;
+ final int t = j*size + i;
+ rat.setFrom(acc);
+ rat.inverseDivide(LU, t, errorLU);
+ rat.storeTo (LU, t, errorLU);
}
}
}
@@ -188,51 +254,87 @@ final class Solver implements Matrix {
* Ensure that the matrix is not singular.
*/
for (int j=0; j<size; j++) {
- if (LU[j*size + j] == 0) {
- throw new NoninvertibleMatrixException();
+ rat.setFrom(LU, j*size + j, errorLU);
+ if (rat.isZero()) {
+ final Integer n = size;
+ throw new NoninvertibleMatrixException(Errors.format(Errors.Keys.NonInvertibleMatrix_2, n, n));
}
}
/*
- * Copy right hand side with pivoting.
- * We will write the result of this method directly in the elements array.
+ * Copy right hand side with pivoting. Write the result directly in the elements array
+ * of the result matrix. This block does not perform floating-point arithmetic operations.
*/
- final double[] elements = new double[size * innerSize];
+ final GeneralMatrix result = GeneralMatrix.createExtendedPrecision(size, innerSize);
+ final double[] elements = result.elements;
+ final int errorOffset = size * innerSize;
for (int k=0,j=0; j<size; j++) {
final int p = pivot[j];
for (int i=0; i<innerSize; i++) {
- elements[k++] = Y.getElement(p, i);
+ if (eltY != null) {
+ final int t = p*innerSize + i;
+ elements[k] = eltY[t];
+ elements[k + errorOffset] = eltY[t + errorOffset];
+ } else {
+ elements[k] = Y.getElement(p, i);
+ }
+ k++;
}
}
/*
- * Solve L*Y = B(pivot, :)
+ * Solve L*Y = B(pivot, :). The inner block is equivalent to the following line,
+ * but using double-double arithmetic instead of 'double' primitive type:
+ *
+ * elements[loRowOffset + i] -= (elements[rowOffset + i] * LU[luRowOffset + k]);
*/
for (int k=0; k<size; k++) {
final int rowOffset = k*innerSize; // Offset of row computed by current iteration.
for (int j=k; ++j < size;) {
- final int loRowOffset = j*innerSize; // Offset of a row after (locate lower) the current row.
- final int luRowOffset = j*size; // Offset of the corresponding row in the LU matrix.
+ final int loRowOffset = j*innerSize; // Offset of some row after the current row.
+ final int luRowOffset = j*size; // Offset of the corresponding row in the LU matrix.
for (int i=0; i<innerSize; i++) {
- elements[loRowOffset + i] -= (elements[rowOffset + i] * LU[luRowOffset + k]);
+ acc.setFrom (elements, loRowOffset + i, errorOffset);
+ rat.setFrom (elements, rowOffset + i, errorOffset);
+ rat.multiply(LU, luRowOffset + k, errorLU);
+ acc.subtract(rat);
+ acc.storeTo (elements, loRowOffset + i, errorOffset);
}
}
}
/*
- * Solve U*X = Y
+ * Solve U*X = Y. The content of the loop is equivalent to the following line,
+ * but using double-double arithmetic instead of 'double' primitive type:
+ *
+ * double sum = LU[k*size + k];
+ * for (int i=0; i<innerSize; i++) {
+ * elements[rowOffset + i] /= sum;
+ * }
+ * for (int j=0; j<k; j++) {
+ * sum = LU[j*size + k];
+ * for (int i=0; i<innerSize; i++) {
+ * elements[upRowOffset + i] -= (elements[rowOffset + i] * sum);
+ * }
+ * }
*/
for (int k=size; --k >= 0;) {
final int rowOffset = k*innerSize; // Offset of row computed by current iteration.
- final double d = LU[k*size + k]; // A diagonal element on the current row.
+ acc.setFrom(LU, k*size + k, errorLU); // A diagonal element on the current row.
for (int i=0; i<innerSize; i++) { // Apply to all columns in the current row.
- elements[rowOffset + i] /= d;
+ rat.setFrom(acc);
+ rat.inverseDivide(elements, rowOffset + i, errorOffset);
+ rat.storeTo (elements, rowOffset + i, errorOffset);
}
for (int j=0; j<k; j++) {
final int upRowOffset = j*innerSize; // Offset of a row before (locate upper) the current row.
- final double c = LU[j*size + k]; // Same column than the diagonal element, but in the upper row.
+ acc.setFrom(LU, j*size + k, errorLU); // Same column than the diagonal element, but in the upper row.
for (int i=0; i<innerSize; i++) { // Apply to all columns in the upper row.
- elements[upRowOffset + i] -= (elements[rowOffset + i] * c);
+ rat.setFrom(elements, rowOffset + i, errorOffset);
+ rat.multiply(acc);
+ rat.subtract(elements, upRowOffset + i, errorOffset);
+ rat.negate();
+ rat.storeTo(elements, upRowOffset + i, errorOffset);
}
}
}
- return Matrices.create(size, innerSize, elements);
+ return result;
}
}
Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/package-info.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/package-info.java?rev=1529262&r1=1529261&r2=1529262&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/package-info.java (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/package-info.java Fri Oct 4 19:08:18 2013
@@ -43,6 +43,20 @@
*
* <p><center><img src="doc-files/AffineTransform.png"></center></p>
*
+ * {@section Extended floating point precision}
+ * This package uses extended floating point precision for most arithmetic operations like matrix multiplications and
+ * inversions. SIS needs extended precision because <cite>affine transforms</cite> concatenations like conversion from
+ * degrees to radians, followed by some operations, followed by conversion back from radians to degrees, are very frequent.
+ * Without extended precision, we often obtain values like 0.99999⦠where we would expect an identity transform.
+ * The usual workaround - namely comparing the floating point values with a small <var>epsilon</var> tolerance value -
+ * is dangerous in this particular case because <cite>datum shifts</cite>, when expressed as a matrix from their
+ * {@linkplain org.apache.sis.referencing.datum.BursaWolfParameters Bursa-Wolf parameters}, are very close to the
+ * identity transform.
+ *
+ * <p>The current implementation uses
+ * <a href="http://en.wikipedia.org/wiki/Double-double_%28arithmetic%29#Double-double_arithmetic">double-double
+ * arithmetic</a>. However this may change in any future SIS version.</p>
+ *
* {@section Related projects}
* This package is <strong>not</strong> designed for large matrices, and is rooted in
* {@code org.apache.sis.referencing} for making clearer that this is not a general-purpose library.
Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/GeneralMatrixTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/GeneralMatrixTest.java?rev=1529262&r1=1529261&r2=1529262&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/GeneralMatrixTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/GeneralMatrixTest.java [UTF-8] Fri Oct 4 19:08:18 2013
@@ -16,7 +16,8 @@
*/
package org.apache.sis.referencing.operation.matrix;
-import org.apache.sis.test.DependsOn;
+import java.util.Random;
+import org.junit.Test;
import static org.junit.Assert.*;
@@ -30,17 +31,19 @@ import static org.junit.Assert.*;
* @version 0.4
* @module
*/
-@DependsOn(SolverTest.class)
public final strictfp class GeneralMatrixTest extends MatrixTestCase {
/**
* Number of rows and columns.
*/
- private final int size;
+ private int size;
/**
- * Creates a test with a random size for the square matrix.
+ * Computes a random size for the next matrix to create.
+ *
+ * @param random The random number generator to use.
*/
- public GeneralMatrixTest() {
+ @Override
+ void prepareNewMatrixSize(final Random random) {
size = 5 + random.nextInt(8); // Matrix sizes from 5 to 12 inclusive.
}
@@ -55,4 +58,39 @@ public final strictfp class GeneralMatri
super.validate(matrix);
assertEquals(GeneralMatrix.class, matrix.getClass());
}
+
+ /**
+ * Tests {@link GeneralMatrix#getExtendedElements(Matrix, int, int, boolean)}.
+ * This test verifies that {@code getExtendedElements} can infer default error
+ * terms for some well known values.
+ *
+ * @see Matrix2Test#testGetExtendedElements()
+ */
+ @Test
+ public void testGetExtendedElements() {
+ testGetExtendedElements(new GeneralMatrix(2, 2, new double[] {
+ StrictMath.PI / 180, // Degrees to radians
+ 180 / StrictMath.PI, // Radians to degrees
+ 0.9, // Gradians to degrees
+ 0.1234567})); // Random value with no special meaning.
+ }
+
+ /**
+ * Implementation of {@link #testGetExtendedElements()} shared by {@link Matrix2Test}.
+ */
+ static void testGetExtendedElements(final MatrixSIS matrix) {
+ final double[] elements = GeneralMatrix.getExtendedElements(matrix, Matrix2.SIZE, Matrix2.SIZE, false);
+ assertArrayEquals(new double[] {
+ // Same values than in the above matrix.
+ StrictMath.PI / 180,
+ 180 / StrictMath.PI,
+ 0.9,
+ 0.1234567,
+
+ // Values below this point are error terms copied from DoubleDouble.ERRORS.
+ 2.9486522708701687E-19,
+ -1.9878495670576283E-15,
+ -2.2204460492503132E-17,
+ 0}, elements, STRICT);
+ }
}
Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/MatricesTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/MatricesTest.java?rev=1529262&r1=1529261&r2=1529262&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/MatricesTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/MatricesTest.java [UTF-8] Fri Oct 4 19:08:18 2013
@@ -331,18 +331,28 @@ public final strictfp class MatricesTest
*/
@Test
public void testToString() {
- final MatrixSIS matrix = Matrices.create(4, 4, new double[] {
- 39.5193682106975150, -68.5200, 80.0, 98,
- -66.0358637477182200, Double.NaN, 43.9, Double.NEGATIVE_INFINITY,
- 2.0741018968776337, 83.7260, 37.0, -3,
- 91.8796187759200600, -18.2674, 24.0, 36
+ assertMultilinesEquals(
+ "┌ ┐\n" +
+ "│ 1 0 0 0 │\n" +
+ "│ 0 1 0 0 │\n" +
+ "│ 0 0 1 0 │\n" +
+ "│ 0 0 0 1 │\n" +
+ "└ ┘\n", new Matrix4().toString());
+ /*
+ * Mix of values with different precision, ±0, ±1, NaN and infinities.
+ */
+ final MatrixSIS matrix = Matrices.create(4, 5, new double[] {
+ 39.5193682106975150, -68.5200, -1.0, 1, 98,
+ -66.0358637477182200, Double.NaN, 43.0, 0, Double.NEGATIVE_INFINITY,
+ 2.0741018968776337, 83.7260, -0.0, 1, -3,
+ 91.8796187759200600, -18.2674, 24.5, 0, 36.5
});
assertMultilinesEquals(
- "┌ ┐\n" +
- "│ 39.5193682106975150 -68.5200 80.0 98.0 │\n" +
- "│ -66.0358637477182200 NaN 43.9 -∞ │\n" +
- "│ 2.0741018968776337 83.7260 37.0 -3.0 │\n" +
- "│ 91.8796187759200600 -18.2674 24.0 36.0 │\n" +
- "└ ┘\n", matrix.toString());
+ "┌ ┐\n" +
+ "│ 39.5193682106975150 -68.5200 -1 1 98.0 │\n" +
+ "│ -66.0358637477182200 NaN 43.0 0 -∞ │\n" +
+ "│ 2.0741018968776337 83.7260 -0 1 -3.0 │\n" +
+ "│ 91.8796187759200600 -18.2674 24.5 0 36.5 │\n" +
+ "└ ┘\n", matrix.toString());
}
}
Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/Matrix1Test.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/Matrix1Test.java?rev=1529262&r1=1529261&r2=1529262&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/Matrix1Test.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/Matrix1Test.java [UTF-8] Fri Oct 4 19:08:18 2013
@@ -55,6 +55,7 @@ public final strictfp class Matrix1Test
*/
@Test
public void testConstructor() {
+ initialize(415870088589607716L);
final double[] elements = createRandomPositiveValues(SIZE * SIZE);
final Matrix1 matrix = new Matrix1(
elements[0]);
Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/Matrix2Test.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/Matrix2Test.java?rev=1529262&r1=1529261&r2=1529262&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/Matrix2Test.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/Matrix2Test.java [UTF-8] Fri Oct 4 19:08:18 2013
@@ -55,6 +55,7 @@ public final strictfp class Matrix2Test
*/
@Test
public void testConstructor() {
+ initialize(-8453835559080304420L);
final double[] elements = createRandomPositiveValues(SIZE * SIZE);
final Matrix2 matrix = new Matrix2(
elements[0],
@@ -64,4 +65,18 @@ public final strictfp class Matrix2Test
validate(matrix);
assertArrayEquals(elements, matrix.getElements(), STRICT);
}
+
+ /**
+ * Tests {@link GeneralMatrix#getExtendedElements(Matrix, int, int, boolean)}.
+ * This test is a copy of {@link GeneralMatrixTest#testGetExtendedElements()},
+ * but on a {@link Matrix2} instance instead of {@link GeneralMatrix}.
+ */
+ @Test
+ public void testGetExtendedElements() {
+ GeneralMatrixTest.testGetExtendedElements(new Matrix2(
+ StrictMath.PI / 180, // Degrees to radians
+ 180 / StrictMath.PI, // Radians to degrees
+ 0.9, // Gradians to degrees
+ 0.1234567)); // Random value with no special meaning.
+ }
}
Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/Matrix3Test.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/Matrix3Test.java?rev=1529262&r1=1529261&r2=1529262&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/Matrix3Test.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/Matrix3Test.java [UTF-8] Fri Oct 4 19:08:18 2013
@@ -56,6 +56,7 @@ public final strictfp class Matrix3Test
*/
@Test
public void testConstructor() {
+ initialize(-2078758443421995879L);
final double[] elements = createRandomPositiveValues(SIZE * SIZE);
final Matrix3 matrix = new Matrix3(
elements[0],
Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/Matrix4Test.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/Matrix4Test.java?rev=1529262&r1=1529261&r2=1529262&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/Matrix4Test.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/Matrix4Test.java [UTF-8] Fri Oct 4 19:08:18 2013
@@ -56,6 +56,7 @@ public final strictfp class Matrix4Test
*/
@Test
public void testConstructor() {
+ initialize(-7053945420932915425L);
final double[] elements = createRandomPositiveValues(SIZE * SIZE);
final Matrix4 matrix = new Matrix4(
elements[ 0],
@@ -77,4 +78,44 @@ public final strictfp class Matrix4Test
validate(matrix);
assertArrayEquals(elements, matrix.getElements(), STRICT);
}
+
+ /**
+ * Tests the accuracy of a chain of matrix operations.
+ *
+ * @throws NoninvertibleMatrixException Should never happen.
+ */
+ @Test
+ public void testAccuracy() throws NoninvertibleMatrixException {
+ final double parisMeridian = 2 + (20 + 13.82/60)/60; // Paris meridian: 2°20'13.82"
+ final double toRadians = StrictMath.PI / 180;
+ /*
+ * Gradians to degrees with a Prime Meridian shift
+ * and a random conversion factor for z values.
+ */
+ final Matrix4 step1 = new Matrix4(
+ 0.9, 0, 0, parisMeridian,
+ 0, 0.9, 0, 0,
+ 0, 0, 0.8, 0, // Random conversion factor for z values.
+ 0, 0, 0, 1);
+ /*
+ * Degrees to radians with swapping of (longitude, latitude) axes
+ * and a conversion factor of z values from feet to metres.
+ */
+ final Matrix4 step2 = new Matrix4(
+ 0, toRadians, 0, 0,
+ toRadians, 0, 0, 0,
+ 0, 0, 0.3048, 0,
+ 0, 0, 0, 1);
+ /*
+ * Converse of the above operations.
+ */
+ final MatrixSIS step3 = step2.multiply(step1).inverse();
+ /*
+ * Concatenate everything, which should go back to the identity transform.
+ * Note that the 'isIdentity()' test fail if the double-double arithmetic is
+ * disabled, because some scale factors will be 0.9999999999999999 instead of 1.
+ */
+ final MatrixSIS result = step3.multiply(step2).multiply(step1);
+ assertTrue("isIdentity()", result.isIdentity());
+ }
}
Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/MatrixTestCase.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/MatrixTestCase.java?rev=1529262&r1=1529261&r2=1529262&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/MatrixTestCase.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/MatrixTestCase.java [UTF-8] Fri Oct 4 19:08:18 2013
@@ -18,6 +18,8 @@ package org.apache.sis.referencing.opera
import java.util.Random;
import Jama.Matrix;
+import org.apache.sis.math.Statistics;
+import org.apache.sis.internal.util.DoubleDouble;
import org.apache.sis.test.TestCase;
import org.apache.sis.test.TestUtilities;
import org.apache.sis.test.DependsOnMethod;
@@ -48,22 +50,47 @@ import static org.apache.sis.test.Assert
*/
public abstract strictfp class MatrixTestCase extends TestCase {
/**
+ * {@code true} for reusing the same sequences of random numbers in every execution of test cases, or
+ * {@code false} for "truly" random sequences of random numbers. This flag can be set to {@code false}
+ * for testing purpose, but should be set to {@code true} otherwise for avoiding random test failure.
+ * This is needed because we want to set {@link #TOLERANCE} to a small value, but it is very difficult
+ * to guaranteed that a random sequence of numbers will not cause a larger discrepancy.
+ *
+ * <p>Note that this flag is set to {@code false} if double-double arithmetic is disabled because in such
+ * case, the results should be identical to the JAMA results (i.e. equal using a {@link #TOLERANCE} of zero)
+ * for any sequence of numbers.</p>
+ */
+ protected static final boolean DETERMINIST = !DoubleDouble.DISABLED;
+
+ /**
* A constant for any test in this class or a subclass which expect
* a floating point value to be strictly equals to an other value.
*/
static final double STRICT = 0;
/**
- * Tolerance factor for comparisons of floating point numbers.
- * The matrix elements used in this class varies between 0 and 100,
+ * Tolerance factor for comparisons of floating point numbers between SIS and JAMA implementation,
+ * which is {@value}. Note that the matrix element values used in this class vary between 0 and 100,
* and the {@code Math.ulp(100.0)} value is approximatively 1.4E-14.
+ *
+ * {@section How this value is determined}
+ * Experience (by looking at {@link #statistics}) shows that the differences are usually smaller than 1E-12.
+ * However when using non-determinist sequence of random values ({@link #DETERMINIST} sets to {@code false}),
+ * we do have from time-to-time a difference around 1E-9.
+ *
+ * Those differences exist because SIS uses double-double arithmetic, while JAMA uses ordinary double.
+ * To remove that ambiguity, one can temporarily set {@link DoubleDouble#DISABLED} to {@code true},
+ * in which case the SIS results should be strictly identical to the JAMA ones.
+ *
+ * @see SolverTest#TOLERANCE
+ * @see NonSquareMatrixTest#printStatistics()
*/
- static final double TOLERANCE = 1E-10;
+ protected static final double TOLERANCE = DoubleDouble.DISABLED ? STRICT : 1E-12;
/**
* Number of random matrices to try in arithmetic operation tests.
*/
- static final int NUMBER_OF_REPETITIONS = 10;
+ static final int NUMBER_OF_REPETITIONS = 100;
/**
* The threshold in matrix determinant for attempting to compute the inverse.
@@ -72,15 +99,45 @@ public abstract strictfp class MatrixTes
private static final double DETERMINANT_THRESHOLD = 0.001;
/**
- * Random number generator, created by {@link #initialize(String, boolean)} when first needed.
+ * Statistics about the different between the JAMA and SIS matrix elements, or {@code null}
+ * if those statistics do not need to be collected. This is used during the test development
+ * phase for tuning the tolerance threshold.
+ *
+ * @see NonSquareMatrixTest#printStatistics()
+ */
+ static final Statistics statistics = verbose ? new Statistics("|SIS - JAMA|") : null;
+
+ /**
+ * Random number generator, created by {@link #initialize(long)} as the first operation of
+ * any test method which will use random numbers. This random number generator will use a
+ * fixed seed if {@link #DETERMINIST} is {@code true}, which is the normal case.
*/
- final Random random;
+ private Random random;
/**
* For subclasses only.
*/
MatrixTestCase() {
- random = TestUtilities.createRandomNumberGenerator();
+ }
+
+ /**
+ * Initializes the random number generator to the given seed. If {@link #DETERMINIST} is {@code false}
+ * (which happen only when performing some more extensive tests), then the given seed will be replaced
+ * by a random one.
+ *
+ * @param seed The initial seed.
+ */
+ final void initialize(final long seed) {
+ random = DETERMINIST ? new Random(seed) : TestUtilities.createRandomNumberGenerator();
+ }
+
+ /**
+ * Computes a random size for the next matrix to create. This method is overridden
+ * only by subclasses that test matrix implementations supporting arbitrary sizes.
+ *
+ * @param random The random number generator to use for computing a random matrix size.
+ */
+ void prepareNewMatrixSize(final Random random) {
}
/** Returns the number of rows of the matrix being tested. */ abstract int getNumRow();
@@ -98,15 +155,27 @@ public abstract strictfp class MatrixTes
/**
* Verifies that the SIS matrix is equals to the JAMA one, up to the given tolerance value.
+ *
+ * @param expected The JAMA matrix used as a reference implementation.
+ * @param actual The SIS matrix to compare to JAMA.
+ * @param tolerance The tolerance threshold, usually either {@link #STRICT} or {@link #TOLERANCE}.
*/
static void assertMatrixEquals(final Matrix expected, final MatrixSIS actual, final double tolerance) {
final int numRow = actual.getNumRow();
final int numCol = actual.getNumCol();
assertEquals("numRow", expected.getRowDimension(), numRow);
assertEquals("numCol", expected.getColumnDimension(), numCol);
+ final String name = actual.getClass().getSimpleName();
for (int j=0; j<numRow; j++) {
for (int i=0; i<numCol; i++) {
- assertEquals(expected.get(j,i), actual.getElement(j,i), tolerance);
+ final double e = expected.get(j,i);
+ final double a = actual.getElement(j,i);
+ assertEquals(name, e, a, tolerance);
+ if (tolerance != STRICT && statistics != null) {
+ synchronized (statistics) {
+ statistics.accept(StrictMath.abs(e - a));
+ }
+ }
}
}
}
@@ -134,6 +203,8 @@ public abstract strictfp class MatrixTes
*/
@Test
public void testGetElements() {
+ initialize(3812872376135347328L);
+ prepareNewMatrixSize(random);
final int numRow = getNumRow();
final int numCol = getNumCol();
final double[] elements = createRandomPositiveValues(numRow * numCol);
@@ -155,6 +226,8 @@ public abstract strictfp class MatrixTes
@Test
@DependsOnMethod("testGetElements")
public void testSetElement() {
+ initialize(-8079924100564483073L);
+ prepareNewMatrixSize(random);
final int numRow = getNumRow();
final int numCol = getNumCol();
final MatrixSIS matrix = Matrices.createZero(numRow, numCol);
@@ -184,6 +257,8 @@ public abstract strictfp class MatrixTes
@Test
@DependsOnMethod("testSetElement")
public void testIsIdentity() {
+ initialize(6173145457052452823L);
+ prepareNewMatrixSize(random);
final int numRow = getNumRow();
final int numCol = getNumCol();
final MatrixSIS matrix = Matrices.createDiagonal(numRow, numCol);
@@ -213,6 +288,8 @@ public abstract strictfp class MatrixTes
@Test
@DependsOnMethod("testSetElement")
public void testCloneEquals() {
+ initialize(-4572234104840706847L);
+ prepareNewMatrixSize(random);
final int numRow = getNumRow();
final int numCol = getNumCol();
final double[] elements = createRandomPositiveValues(numRow * numCol);
@@ -241,6 +318,8 @@ public abstract strictfp class MatrixTes
@Test
@DependsOnMethod("testGetElements")
public void testTranspose() {
+ initialize(585037875560696050L);
+ prepareNewMatrixSize(random);
final int numRow = getNumRow();
final int numCol = getNumCol();
final double[] elements = createRandomPositiveValues(numRow * numCol);
@@ -260,6 +339,8 @@ public abstract strictfp class MatrixTes
@Test
@DependsOnMethod("testGetElements")
public void testNormalizeColumns() {
+ initialize(1549772118153010333L);
+ prepareNewMatrixSize(random);
final int numRow = getNumRow();
final int numCol = getNumCol();
final double[] elements = createRandomPositiveValues(numRow * numCol);
@@ -273,7 +354,7 @@ public abstract strictfp class MatrixTes
m += e*e;
}
m = StrictMath.sqrt(m);
- assertEquals(1, m, TOLERANCE);
+ assertEquals(1, m, 1E-12);
}
}
@@ -283,9 +364,11 @@ public abstract strictfp class MatrixTes
@Test
@DependsOnMethod("testGetElements")
public void testMultiply() {
- final int numRow = getNumRow();
- final int numCol = getNumCol();
+ initialize(2478887638739725150L);
for (int n=0; n<NUMBER_OF_REPETITIONS; n++) {
+ prepareNewMatrixSize(random);
+ final int numRow = getNumRow();
+ final int numCol = getNumCol();
double[] elements = createRandomPositiveValues(numRow * numCol);
final MatrixSIS matrix = Matrices.create(numRow, numCol, elements);
final Matrix reference = new Matrix(elements, numCol).transpose();
@@ -318,12 +401,11 @@ public abstract strictfp class MatrixTes
@Test
@DependsOnMethod("testMultiply")
public void testSolve() throws NoninvertibleMatrixException {
- final int numRow = getNumRow();
- final int numCol = getNumCol();
-
- if (numRow != 1 || numCol != 1) return; // Temporary limitation.
-
+ initialize(2108474073121762243L);
for (int n=0; n<NUMBER_OF_REPETITIONS; n++) {
+ prepareNewMatrixSize(random);
+ final int numRow = getNumRow();
+ final int numCol = getNumCol();
double[] elements = createRandomPositiveValues(numRow * numCol);
final Matrix reference = new Matrix(elements, numCol).transpose();
if (!(reference.det() >= DETERMINANT_THRESHOLD)) {
@@ -347,7 +429,7 @@ public abstract strictfp class MatrixTes
*/
final Matrix referenceResult = reference.solve(referenceArg);
final MatrixSIS matrixResult = matrix.solve(matrixArg);
- assertMatrixEquals(referenceResult, matrixResult, TOLERANCE);
+ assertMatrixEquals(referenceResult, matrixResult, SolverTest.TOLERANCE);
}
}
@@ -360,9 +442,11 @@ public abstract strictfp class MatrixTes
@Test
@DependsOnMethod("testSolve")
public void testInverse() throws NoninvertibleMatrixException {
- final int numRow = getNumRow();
- final int numCol = getNumCol();
+ initialize(-9063921123024549789L);
for (int n=0; n<NUMBER_OF_REPETITIONS; n++) {
+ prepareNewMatrixSize(random);
+ final int numRow = getNumRow();
+ final int numCol = getNumCol();
final double[] elements = createRandomPositiveValues(numRow * numCol);
final Matrix reference = new Matrix(elements, numCol).transpose();
if (!(reference.det() >= DETERMINANT_THRESHOLD)) {
@@ -378,6 +462,8 @@ public abstract strictfp class MatrixTes
*/
@Test
public void testSerialization() {
+ initialize(-3232759118744327281L);
+ prepareNewMatrixSize(random);
final int numRow = getNumRow();
final int numCol = getNumCol();
final MatrixSIS matrix = Matrices.create(numRow, numCol, createRandomPositiveValues(numRow * numCol));
Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/NonSquareMatrixTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/NonSquareMatrixTest.java?rev=1529262&r1=1529261&r2=1529262&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/NonSquareMatrixTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/NonSquareMatrixTest.java [UTF-8] Fri Oct 4 19:08:18 2013
@@ -16,7 +16,10 @@
*/
package org.apache.sis.referencing.operation.matrix;
+import java.util.Random;
import org.apache.sis.test.DependsOn;
+import org.apache.sis.test.TestUtilities;
+import org.junit.AfterClass;
import static org.junit.Assert.*;
@@ -25,22 +28,31 @@ import static org.junit.Assert.*;
* Tests the {@link NonSquareMatrix} implementation.
* This class inherits all tests defined in {@link MatrixTestCase}.
*
+ * <p>This class is expected to be the last {@code MatrixTestCase} subclass to be executed,
+ * because it sends the {@link #statistics} to {@link #out}. This condition is ensured if
+ * the tests are executed by {@link org.apache.sis.test.suite.ReferencingTestSuite}.
+ * However it is not a big deal if this condition is broken, as the only consequence
+ * is that reported statistics will be incomplete.</p>
+ *
* @author Martin Desruisseaux (Geomatys)
* @since 0.4
* @version 0.4
* @module
*/
-@DependsOn(GeneralMatrixTest.class)
+@DependsOn(SolverTest.class)
public final strictfp class NonSquareMatrixTest extends MatrixTestCase {
/**
* Number of rows and columns, initialized by {@link #initialize(String, boolean)}.
*/
- private final int numRow, numCol;
+ private int numRow, numCol;
/**
- * Creates a test with a random size for the matrix and ensure that the matrix is not square.
+ * Computes a random size for the next matrix to create.
+ *
+ * @param random The random number generator to use.
*/
- public NonSquareMatrixTest() {
+ @Override
+ void prepareNewMatrixSize(final Random random) {
numRow = 5 + random.nextInt(8); // Matrix sizes from 5 to 12 inclusive.
int n;
do n = 5 + random.nextInt(8);
@@ -67,4 +79,27 @@ public final strictfp class NonSquareMat
@org.junit.Ignore
public void testInverse() throws NoninvertibleMatrixException {
}
+
+ /**
+ * TODO: inverse transform not yet implemented for non-square matrix.
+ */
+ @Override
+ @org.junit.Ignore
+ public void testSolve() throws NoninvertibleMatrixException {
+ }
+
+ /**
+ * Prints the statistics about the differences between JAMA and SIS matrix elements.
+ * Those statistics will be visible only if {@link #verbose} is {@code true}.
+ */
+ @AfterClass
+ public static void printStatistics() {
+ if (statistics != null) {
+ TestUtilities.printSeparator("Overall statistics on agreement of matrix arithmetic");
+ synchronized (statistics) {
+ out.println(statistics);
+ }
+ TestUtilities.forceFlushOutput();
+ }
+ }
}
Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/SolverTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/SolverTest.java?rev=1529262&r1=1529261&r2=1529262&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/SolverTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/SolverTest.java [UTF-8] Fri Oct 4 19:08:18 2013
@@ -18,6 +18,7 @@ package org.apache.sis.referencing.opera
import java.util.Random;
import Jama.Matrix;
+import org.apache.sis.test.DependsOn;
import org.apache.sis.test.TestUtilities;
import org.apache.sis.test.TestCase;
import org.junit.Test;
@@ -27,13 +28,36 @@ import org.junit.Test;
* Tests the {@link Solver} class using <a href="http://math.nist.gov/javanumerics/jama">JAMA</a>
* as the reference implementation.
*
+ * {@section Cyclic dependency}
+ * There is a cyclic test dependency since {@link GeneralMatrix} needs {@link Solver} for some operations,
+ * and conversely. To be more specific the dependency order is:
+ *
+ * <ol>
+ * <li>Simple {@link GeneralMatrix} methods (construction, get/set elements)</li>
+ * <li>{@link Solver}</li>
+ * <li>More complex {@code GeneralMatrix} methods (matrix inversion, solve)</li>
+ * </ol>
+ *
+ * We test {@code GeneralMatrix} before {@code Solver} since nothing could be done without
+ * the above-cited simple operations anyway.
+ *
* @author Martin Desruisseaux (Geomatys)
* @since 0.4
* @version 0.4
* @module
*/
+@DependsOn(GeneralMatrixTest.class) // See class javadoc
public final strictfp class SolverTest extends TestCase {
/**
+ * The tolerance threshold for this test case, which is {@value}. This value needs to be higher then the
+ * {@link MatrixTestCase#TOLERANCE} one because of the increased complexity of {@link Solver} operations.
+ *
+ * @see MatrixTestCase#TOLERANCE
+ * @see NonSquareMatrixTest#printStatistics()
+ */
+ protected static final double TOLERANCE = 100 * MatrixTestCase.TOLERANCE;
+
+ /**
* The matrix to test.
*/
private MatrixSIS matrix;
@@ -48,7 +72,7 @@ public final strictfp class SolverTest e
* Initializes the {@link #matrix} and {@link #reference} matrices to random values.
*/
private void createMatrices(final int numRow, final int numCol, final Random random) {
- matrix = new GeneralMatrix(numRow, numCol, false);
+ matrix = new GeneralMatrix(numRow, numCol, false, 1);
reference = new Matrix(numRow, numCol);
for (int j=0; j<numRow; j++) {
for (int i=0; i<numCol; i++) {
@@ -66,7 +90,12 @@ public final strictfp class SolverTest e
*/
@Test
public void testSolve() throws NoninvertibleMatrixException {
- final Random random = TestUtilities.createRandomNumberGenerator();
+ final Random random;
+ if (MatrixTestCase.DETERMINIST) {
+ random = new Random(7671901444622173417L);
+ } else {
+ random = TestUtilities.createRandomNumberGenerator();
+ }
for (int k=0; k<MatrixTestCase.NUMBER_OF_REPETITIONS; k++) {
final int size = random.nextInt(16) + 1;
createMatrices(size, random.nextInt(16) + 1, random);
@@ -80,8 +109,8 @@ public final strictfp class SolverTest e
out.println(e); // "Matrix is singular."
continue;
}
- final MatrixSIS U = Solver.solve(matrix, matrixArg, matrixArg.getNumCol());
- MatrixTestCase.assertMatrixEquals(jama, U, MatrixTestCase.TOLERANCE);
+ final MatrixSIS U = Solver.solve(matrix, matrixArg);
+ MatrixTestCase.assertMatrixEquals(jama, U, TOLERANCE);
}
}
}
Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java?rev=1529262&r1=1529261&r2=1529262&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java [UTF-8] Fri Oct 4 19:08:18 2013
@@ -31,13 +31,13 @@ import org.junit.BeforeClass;
*/
@Suite.SuiteClasses({
// Test matrix first because they may be used in about every SIS corners.
+ org.apache.sis.referencing.operation.matrix.GeneralMatrixTest.class,
org.apache.sis.referencing.operation.matrix.SolverTest.class,
org.apache.sis.referencing.operation.matrix.Matrix1Test.class,
org.apache.sis.referencing.operation.matrix.Matrix2Test.class,
org.apache.sis.referencing.operation.matrix.Matrix3Test.class,
org.apache.sis.referencing.operation.matrix.Matrix4Test.class,
- org.apache.sis.referencing.operation.matrix.GeneralMatrixTest.class,
- org.apache.sis.referencing.operation.matrix.NonSquareMatrixTest.class,
+ org.apache.sis.referencing.operation.matrix.NonSquareMatrixTest.class, // Expected to be last MatrixTestCase - see javadoc.
org.apache.sis.referencing.operation.matrix.MatricesTest.class,
org.apache.sis.referencing.operation.matrix.AffineTransforms2DTest.class,
Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/Context.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/Context.java?rev=1529262&r1=1529261&r2=1529262&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/Context.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/Context.java [UTF-8] Fri Oct 4 19:08:18 2013
@@ -25,6 +25,9 @@ import org.apache.sis.util.Version;
import org.apache.sis.util.Exceptions;
import org.apache.sis.util.logging.Logging;
import org.apache.sis.util.logging.WarningListener;
+import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.resources.Messages;
+import org.apache.sis.util.resources.IndexedResourceBundle;
import org.apache.sis.xml.MarshalContext;
import org.apache.sis.xml.ValueConverter;
import org.apache.sis.xml.ReferenceResolver;
@@ -401,12 +404,42 @@ public final class Context extends Marsh
}
/**
+ * Convenience method for sending a warning for the given message from the {@link Errors} or {@link Messages}
+ * resources. The message will be logged at {@link Level#WARNING}.
+ *
+ * @param context The current context, or {@code null} if none.
+ * @param source The object that emitted a warning. Can not be null.
+ * @param classe The class to declare as the warning source.
+ * @param method The name of the method to declare as the warning source.
+ * @param resources Either {@code Errors.class} or {@code Messages.class}.
+ * @param key The resource keys as one of the constants defined in the {@code Keys} inner class.
+ * @param arguments The arguments to be given to {@code MessageFormat} for formatting the log message.
+ */
+ public static void warningOccured(final Context context, final Object source, final Class<?> classe, final String method,
+ final Class<? extends IndexedResourceBundle> resources, final int key, final Object... arguments)
+ {
+ final Locale locale = context != null ? context.getLocale() : null;
+ final IndexedResourceBundle bundle;
+ if (resources == Errors.class) {
+ bundle = Errors.getResources(locale);
+ } else if (resources == Messages.class) {
+ bundle = Messages.getResources(locale);
+ } else {
+ throw new IllegalArgumentException(String.valueOf(resources));
+ }
+ final LogRecord warning = bundle.getLogRecord(Level.WARNING, key, arguments);
+ warning.setSourceClassName(classe.getCanonicalName());
+ warning.setSourceMethodName(method);
+ warningOccured(context, source, warning);
+ }
+
+ /**
* Convenience method for sending a warning for the given exception.
* The logger will be {@code "org.apache.sis.xml"}.
*
* @param context The current context, or {@code null} if none.
* @param source The object that emitted a warning. Can not be null.
- * @param classe The name of the class to declare as the warning source.
+ * @param classe The class to declare as the warning source.
* @param method The name of the method to declare as the warning source.
* @param cause The exception which occurred.
* @param warning {@code true} for {@link Level#WARNING}, or {@code false} for {@link Level#FILE}.
Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_CharacterString.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_CharacterString.java?rev=1529262&r1=1529261&r2=1529262&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_CharacterString.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_CharacterString.java [UTF-8] Fri Oct 4 19:08:18 2013
@@ -16,8 +16,6 @@
*/
package org.apache.sis.internal.jaxb.gco;
-import java.util.logging.Level;
-import java.util.logging.LogRecord;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlSeeAlso;
@@ -137,13 +135,8 @@ public class GO_CharacterString {
property = type;
noset = true;
}
- final Context context = Context.current();
- final LogRecord record = Messages.getResources(context != null ? context.getLocale() : null)
- .getLogRecord(Level.WARNING, Messages.Keys.DiscardedExclusiveProperty_2,
- NAMES[discarded], NAMES[property]);
- record.setSourceClassName(getClass().getCanonicalName());
- record.setSourceMethodName("setText");
- Context.warningOccured(context, value, record);
+ Context.warningOccured(Context.current(), value, getClass(), "setText",
+ Messages.class, Messages.Keys.DiscardedExclusiveProperty_2, NAMES[discarded], NAMES[property]);
if (noset) {
return;
}
Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/Measure.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/Measure.java?rev=1529262&r1=1529261&r2=1529262&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/Measure.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/Measure.java [UTF-8] Fri Oct 4 19:08:18 2013
@@ -19,8 +19,6 @@ package org.apache.sis.internal.jaxb.gco
import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
-import java.util.logging.Level;
-import java.util.logging.LogRecord;
import javax.measure.unit.Unit;
import javax.measure.unit.NonSI;
import javax.xml.bind.annotation.XmlValue;
@@ -165,11 +163,8 @@ public final class Measure {
*/
public void setUnit(final Unit<?> newUnit) {
if (unit != null && !unit.equals(newUnit)) {
- final LogRecord record = Errors.getResources(null)
- .getLogRecord(Level.WARNING, Errors.Keys.IncompatiblePropertyValue_1, unit);
- record.setSourceClassName(getClass().getName());
- record.setSourceMethodName("setUnit");
- Context.warningOccured(Context.current(), this, record);
+ Context.warningOccured(Context.current(), this, getClass(), "setUnit",
+ Errors.class, Errors.Keys.IncompatiblePropertyValue_1, unit);
}
unit = newUnit;
}
Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/ObjectReference.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/ObjectReference.java?rev=1529262&r1=1529261&r2=1529262&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/ObjectReference.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/ObjectReference.java [UTF-8] Fri Oct 4 19:08:18 2013
@@ -17,8 +17,6 @@
package org.apache.sis.internal.jaxb.gco;
import java.util.UUID;
-import java.util.logging.Level;
-import java.util.logging.LogRecord;
import org.apache.sis.xml.XLink;
import org.apache.sis.xml.IdentifierMap;
import org.apache.sis.xml.IdentifierSpace;
@@ -150,11 +148,8 @@ final class ObjectReference {
if (value != null) {
final T previous = map.putSpecialized(authority, value);
if (previous != null && !previous.equals(value)) {
- final LogRecord record = Errors.getResources((context != null) ? context.getLocale() : null)
- .getLogRecord(Level.WARNING, Errors.Keys.InconsistentAttribute_2, authority.getName(), value);
- record.setSourceClassName(IdentifierMap.class.getName());
- record.setSourceMethodName("putSpecialized");
- Context.warningOccured(context, map, record);
+ Context.warningOccured(context, map, IdentifierMap.class, "putSpecialized",
+ Errors.class, Errors.Keys.InconsistentAttribute_2, authority.getName(), value);
map.putSpecialized(authority, previous);
}
}
Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/system/Supervisor.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/system/Supervisor.java?rev=1529262&r1=1529261&r2=1529262&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/system/Supervisor.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/system/Supervisor.java [UTF-8] Fri Oct 4 19:08:18 2013
@@ -21,7 +21,8 @@ import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import java.util.ResourceBundle;
-import java.util.logging.Logger;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
import javax.management.ObjectName;
import javax.management.StandardMBean;
import javax.management.MBeanServer;
@@ -32,12 +33,14 @@ import javax.management.MBeanParameterIn
import javax.management.MBeanConstructorInfo;
import javax.management.JMException;
import javax.management.NotCompliantMBeanException;
+import javax.management.InstanceAlreadyExistsException;
import java.lang.management.ManagementFactory;
import org.apache.sis.setup.About;
import org.apache.sis.util.Localized;
import org.apache.sis.util.logging.Logging;
import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.resources.Messages;
import org.apache.sis.util.collection.TreeTable;
@@ -87,8 +90,13 @@ public final class Supervisor extends St
final ObjectName n = new ObjectName(NAME);
server.registerMBean(new Supervisor(null, null), n);
name = n; // Store only on success.
+ } catch (InstanceAlreadyExistsException e) {
+ final LogRecord record = Messages.getResources(null)
+ .getLogRecord(Level.CONFIG, Messages.Keys.AlreadyRegistered_2, "MBean", NAME);
+ record.setLoggerName("org.apache.sis");
+ Logging.log(Supervisor.class, "register", record);
} catch (Exception e) { // (SecurityException | JMException) on the JDK7 branch.
- Logging.unexpectedException(Logger.getLogger("org.apache.sis"), Supervisor.class, "register", e);
+ Logging.unexpectedException(Logging.getLogger("org.apache.sis"), Supervisor.class, "register", e);
}
}
}
Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/CheckedArrayList.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/CheckedArrayList.java?rev=1529262&r1=1529261&r2=1529262&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/CheckedArrayList.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/CheckedArrayList.java [UTF-8] Fri Oct 4 19:08:18 2013
@@ -17,9 +17,12 @@
package org.apache.sis.internal.util;
import java.util.List;
+import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import org.apache.sis.internal.jaxb.Context;
+import org.apache.sis.util.ArraysExt;
import org.apache.sis.util.resources.Errors;
import org.apache.sis.util.collection.CheckedContainer;
@@ -42,7 +45,7 @@ import static org.apache.sis.util.Argume
*
* @author Martin Desruisseaux (Geomatys)
* @since 0.3 (derived from geotk-2.1)
- * @version 0.3
+ * @version 0.4
* @module
*
* @see Collections#checkedList(List, Class)
@@ -90,30 +93,83 @@ public final class CheckedArrayList<E> e
}
/**
+ * Returns {@code true} if a unmarshalling process is under way.
+ * In the later case, logs a warning for non-null element of the wrong type.
+ *
+ * @see <a href="https://issues.apache.org/jira/browse/SIS-139">SIS-139</a>
+ */
+ static boolean warning(final Collection<?> source, final Object element, final Class<?> type) {
+ final Context context = Context.current();
+ if (context == null) {
+ return false;
+ }
+ if (element != null) {
+ Context.warningOccured(context, source, source.getClass(), "add",
+ Errors.class, Errors.Keys.IllegalArgumentClass_3, "element", type, element.getClass());
+ }
+ return true;
+ }
+
+ /**
* Ensures that the given element is non-null and assignable to the type
* specified at construction time.
*
* @param element the object to check, or {@code null}.
+ * @return {@code true} if the instance is valid, {@code false} if it shall be ignored.
* @throws IllegalArgumentException if the specified element can not be added to this list.
*/
- private void ensureValid(final E element) throws IllegalArgumentException {
- if (!type.isInstance(element)) {
- ensureNonNull("element", element);
- throw new IllegalArgumentException(Errors.format(
- Errors.Keys.IllegalArgumentClass_3, "element", type, element.getClass()));
+ private boolean ensureValid(final E element) throws IllegalArgumentException {
+ if (type.isInstance(element)) {
+ return true;
}
+ if (warning(this, element, type)) {
+ /*
+ * If a unmarshalling process is under way, silently discard null element.
+ * This case happen when a XML element for a collection contains no child.
+ * See https://issues.apache.org/jira/browse/SIS-139
+ */
+ return false;
+ }
+ ensureNonNull("element", element);
+ throw new IllegalArgumentException(Errors.format(
+ Errors.Keys.IllegalArgumentClass_3, "element", type, element.getClass()));
}
/**
* Ensures that all elements of the given collection can be added to this list.
*
* @param collection the collection to check, or {@code null}.
+ * @return The potentially filtered collection of elements to add.
* @throws IllegalArgumentException if at least one element can not be added to this list.
*/
- private void ensureValidCollection(final Collection<? extends E> collection) throws IllegalArgumentException {
- for (final E element : collection) {
- ensureValid(element);
+ @SuppressWarnings("unchecked")
+ private List<E> ensureValidCollection(final Collection<? extends E> collection) throws IllegalArgumentException {
+ int count = 0;
+ final Object[] array = collection.toArray();
+ for (int i=0; i<array.length; i++) {
+ final Object element = array[i];
+ if (ensureValid((E) element)) {
+ array[count++] = element;
+ }
}
+ // Not-so-unsafe cast: we verified in the above loop that all elements are instance of E.
+ // The array itself may not be an instance of E[], but this is not important for Mediator.
+ return new Mediator<E>(ArraysExt.resize((E[]) array, count));
+ }
+
+ /**
+ * A wrapper around the given array for use by {@link #addAll(Collection)} only. This wrapper violates
+ * some {@link List} method contracts, so it must really be used only as a temporary object for passing
+ * the array to {@code AbstractList.addAll(…)} implementation. In particular {@link #toArray()} returns
+ * directly the internal array, because this is the method to be invoked by {@code addAll(…)} (this is
+ * actually the only important method for this wrapper).
+ */
+ private static final class Mediator<E> extends AbstractList<E> {
+ private final E[] array;
+ Mediator(final E[] array) {this.array = array;}
+ @Override public int size() {return array.length;}
+ @Override public E get(int index) {return array[index];}
+ @Override public E[] toArray() {return array;} // See class javadoc.
}
/**
@@ -127,8 +183,10 @@ public final class CheckedArrayList<E> e
*/
@Override
public E set(final int index, final E element) throws IllegalArgumentException {
- ensureValid(element);
- return super.set(index, element);
+ if (ensureValid(element)) {
+ return super.set(index, element);
+ }
+ return get(index);
}
/**
@@ -140,8 +198,10 @@ public final class CheckedArrayList<E> e
*/
@Override
public boolean add(final E element) throws IllegalArgumentException {
- ensureValid(element);
- return super.add(element);
+ if (ensureValid(element)) {
+ return super.add(element);
+ }
+ return false;
}
/**
@@ -154,8 +214,9 @@ public final class CheckedArrayList<E> e
*/
@Override
public void add(final int index, final E element) throws IllegalArgumentException {
- ensureValid(element);
- super.add(index, element);
+ if (ensureValid(element)) {
+ super.add(index, element);
+ }
}
/**
@@ -168,8 +229,7 @@ public final class CheckedArrayList<E> e
*/
@Override
public boolean addAll(final Collection<? extends E> collection) throws IllegalArgumentException {
- ensureValidCollection(collection);
- return super.addAll(collection);
+ return super.addAll(ensureValidCollection(collection));
}
/**
@@ -183,7 +243,6 @@ public final class CheckedArrayList<E> e
*/
@Override
public boolean addAll(final int index, final Collection<? extends E> collection) throws IllegalArgumentException {
- ensureValidCollection(collection);
- return super.addAll(index, collection);
+ return super.addAll(index, ensureValidCollection(collection));
}
}
Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/CheckedHashSet.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/CheckedHashSet.java?rev=1529262&r1=1529261&r2=1529262&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/CheckedHashSet.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/CheckedHashSet.java [UTF-8] Fri Oct 4 19:08:18 2013
@@ -41,7 +41,7 @@ import static org.apache.sis.util.Argume
*
* @author Martin Desruisseaux (Geomatys)
* @since 0.3 (derived from geotk-2.1)
- * @version 0.3
+ * @version 0.4
* @module
*
* @see Collections#checkedSet(Set, Class)
@@ -98,6 +98,14 @@ public final class CheckedHashSet<E> ext
@Override
public boolean add(final E element) throws IllegalArgumentException {
if (!type.isInstance(element)) {
+ if (CheckedArrayList.warning(this, element, type)) {
+ /*
+ * If a unmarshalling process is under way, silently discard null element.
+ * This case happen when a XML element for a collection contains no child.
+ * See https://issues.apache.org/jira/browse/SIS-139
+ */
+ return false;
+ }
ensureNonNull("element", element);
throw new IllegalArgumentException(Errors.format(
Errors.Keys.IllegalArgumentClass_3, "element", type, element.getClass()));
Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/UnmodifiableArrayList.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/UnmodifiableArrayList.java?rev=1529262&r1=1529261&r2=1529262&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/UnmodifiableArrayList.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/UnmodifiableArrayList.java [UTF-8] Fri Oct 4 19:08:18 2013
@@ -358,6 +358,15 @@ public class UnmodifiableArrayList<E> ex
}
/**
+ * Returns a copy of the backing array. Note that the array type is {@code E[]} rather than {@code Object[]}.
+ * This is not what {@code ArrayList} does, but is not forbidden by {@link List#toArray()} javadoc neither.
+ */
+ @Override
+ public E[] toArray() {
+ return array.clone();
+ }
+
+ /**
* Compares this list with the given object for equality.
*
* @param object The object to compare with this list.
Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java?rev=1529262&r1=1529261&r2=1529262&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java [UTF-8] Fri Oct 4 19:08:18 2013
@@ -27,6 +27,7 @@ import static java.lang.Float.intBitsToF
import static java.lang.Float.floatToRawIntBits;
import static java.lang.Double.longBitsToDouble;
import static java.lang.Double.doubleToRawLongBits;
+import org.apache.sis.internal.util.DoubleDouble;
import static org.apache.sis.internal.util.Numerics.SIGN_BIT_MASK;
@@ -173,27 +174,32 @@ public final class MathFunctions extends
int i = vector.length;
// If every elements in the array are zero, returns zero.
- double sum;
+ double v1;
do if (i == 0) return 0;
- while ((sum = vector[--i]) == 0);
+ while ((v1 = vector[--i]) == 0);
// We have found a non-zero element. If it is the only one, returns it directly.
- double v;
- do if (i == 0) return Math.abs(sum);
- while ((v = vector[--i]) == 0);
-
- // If there is exactly 2 elements, use Math.hypot which is more robust than our algorithm.
double v2;
- do if (i == 0) return Math.hypot(sum, v);
+ do if (i == 0) return Math.abs(v1);
while ((v2 = vector[--i]) == 0);
- // Usual magnitude computation.
- sum = sum*sum + v*v + v2*v2;
+ // If there is exactly 2 elements, use Math.hypot which is more robust than our algorithm.
+ double v3;
+ do if (i == 0) return Math.hypot(v1, v2);
+ while ((v3 = vector[--i]) == 0);
+
+ // Usual magnitude computation, but using double-double arithmetic.
+ final DoubleDouble sum = new DoubleDouble();
+ final DoubleDouble dot = new DoubleDouble();
+ sum.setToProduct(v1, v1);
+ dot.setToProduct(v2, v2); sum.add(dot);
+ dot.setToProduct(v3, v3); sum.add(dot);
while (i != 0) {
- v = vector[--i];
- sum += v*v;
+ v1 = vector[--i];
+ dot.setToProduct(v1, v1);
+ sum.add(dot);
}
- return Math.sqrt(sum);
+ return Math.sqrt(sum.value);
}
/**
Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/ArraysExt.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/ArraysExt.java?rev=1529262&r1=1529261&r2=1529262&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/ArraysExt.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/ArraysExt.java [UTF-8] Fri Oct 4 19:08:18 2013
@@ -1715,6 +1715,24 @@ public final class ArraysExt extends Sta
}
/**
+ * Swaps the elements at the given indices in the given array of {@code Object} values.
+ *
+ * {@note While trivial, this method is provided because its need occurs relatively often
+ * and the availability of a <code>swap</code> method makes the code easier to read.}
+ *
+ * @param data The array in which to swap elements.
+ * @param i0 Index of one element to be swapped.
+ * @param i1 Index of the other element to be swapped.
+ *
+ * @since 0.4
+ */
+ public static void swap(final Object[] data, final int i0, final int i1) {
+ final Object t = data[i0];
+ data[i0] = data[i1];
+ data[i1] = t;
+ }
+
+ /**
* Swaps the elements at the given indices in the given array of {@code double} values.
*
* {@note While trivial, this method is provided because its need occurs relatively often
Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java?rev=1529262&r1=1529261&r2=1529262&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java [UTF-8] Fri Oct 4 19:08:18 2013
@@ -47,7 +47,7 @@ import org.apache.sis.util.Classes;
*
* @author Martin Desruisseaux (Geomatys)
* @since 0.3 (derived from geotk-2.4)
- * @version 0.3
+ * @version 0.4
* @module
*/
public final class Logging extends Static {
@@ -178,13 +178,14 @@ public final class Logging extends Stati
* This convenience method performs the following steps:
*
* <ul>
- * <li>Get the logger using {@link #getLogger(Class)};</li>
+ * <li>Unconditionally {@linkplain LogRecord#setSourceClassName(String) set the source class name}
+ * to the {@linkplain Class#getCanonicalName() canonical name} of the given class;</li>
+ * <li>Unconditionally {@linkplain LogRecord#setSourceMethodName(String) set the source method name}
+ * to the given value;</li>
+ * <li>Get the logger for the {@linkplain LogRecord#getLoggerName() logger name} if specified,
+ * or using {@link #getLogger(Class)} otherwise;</li>
* <li>{@linkplain LogRecord#setLoggerName(String) Set the logger name} of the given record,
* if not already set;</li>
- * <li>Unconditionally {@linkplain LogRecord#setSourceClassName(String) set the source class
- * name} to the {@linkplain Class#getCanonicalName() canonical name} of the given class;</li>
- * <li>Unconditionally {@linkplain LogRecord#setSourceMethodName(String) set the source method
- * name} to the given value;</li>
* <li>{@linkplain Logger#log(LogRecord) Log} the modified record.</li>
* </ul>
*
@@ -195,9 +196,13 @@ public final class Logging extends Stati
public static void log(final Class<?> classe, final String method, final LogRecord record) {
record.setSourceClassName(classe.getCanonicalName());
record.setSourceMethodName(method);
- final Logger logger = getLogger(classe);
- if (record.getLoggerName() == null) {
+ final String loggerName = record.getLoggerName();
+ final Logger logger;
+ if (loggerName == null) {
+ logger = getLogger(classe);
record.setLoggerName(logger.getName());
+ } else {
+ logger = getLogger(loggerName);
}
logger.log(record);
}
Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java?rev=1529262&r1=1529261&r2=1529262&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java [UTF-8] Fri Oct 4 19:08:18 2013
@@ -456,6 +456,11 @@ public final class Errors extends Indexe
public static final int NonInvertibleConversion = 82;
/**
+ * Non invertible {0}×{1} matrix.
+ */
+ public static final int NonInvertibleMatrix_2 = 124;
+
+ /**
* Transform is not invertible.
*/
public static final int NonInvertibleTransform = 83;
Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties?rev=1529262&r1=1529261&r2=1529262&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties [ISO-8859-1] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties [ISO-8859-1] Fri Oct 4 19:08:18 2013
@@ -98,6 +98,7 @@ NodeIsLeaf_1 = Node \
NodeNotFound_1 = No \u201c{0}\u201d node found.
NonEquilibratedParenthesis_2 = Missing a \u2018{1}\u2019 parenthesis in \u201c{0}\u201d.
NonInvertibleConversion = Conversion is not invertible.
+NonInvertibleMatrix_2 = Non invertible {0}\u00d7{1} matrix.
NonInvertibleTransform = Transform is not invertible.
NonAngularUnit_1 = \u201c{0}\u201d is not an angular unit.
NonLinearUnit_1 = \u201c{0}\u201d is not a linear unit.
Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties?rev=1529262&r1=1529261&r2=1529262&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties [ISO-8859-1] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties [ISO-8859-1] Fri Oct 4 19:08:18 2013
@@ -88,6 +88,7 @@ NodeIsLeaf_1 = Le n\u
NodeNotFound_1 = Aucun n\u0153ud \u201c{0}\u201d n\u2019a \u00e9t\u00e9 trouv\u00e9.
NonEquilibratedParenthesis_2 = Il manque une parenth\u00e8se \u2018{1}\u2019 dans \u201c{0}\u201d.
NonInvertibleConversion = La conversion n\u2019est pas inversible.
+NonInvertibleMatrix_2 = Matrice {0}\u00d7{1} non inversible.
NonInvertibleTransform = La transformation n\u2019est pas inversible.
NonAngularUnit_1 = \u201c{0}\u201d n\u2019est pas une unit\u00e9 d\u2019angles.
NonLinearUnit_1 = \u201c{0}\u201d n\u2019est pas une unit\u00e9 de longueurs.
Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java?rev=1529262&r1=1529261&r2=1529262&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java [UTF-8] Fri Oct 4 19:08:18 2013
@@ -55,6 +55,11 @@ public final class Messages extends Inde
}
/**
+ * {0} “{1}” is already registered. The second instance will be ignored.
+ */
+ public static final int AlreadyRegistered_2 = 5;
+
+ /**
* Changed the container capacity from {0} to {1} elements.
*/
public static final int ChangedContainerCapacity_2 = 0;
Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.properties
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.properties?rev=1529262&r1=1529261&r2=1529262&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.properties [ISO-8859-1] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.properties [ISO-8859-1] Fri Oct 4 19:08:18 2013
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+AlreadyRegistered_2 = {0} \u201c{1}\u201d is already registered. The second instance will be ignored.
ChangedContainerCapacity_2 = Changed the container capacity from {0} to {1} elements.
DiscardedExclusiveProperty_2 = Property \u201c{0}\u201d has been discarded in favor of \u201c{1}\u201d, because those two properties are mutually exclusive.
PropertyHiddenBy_2 = Property \u201c{0}\u201d is hidden by \u201c{1}\u201d.
Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages_fr.properties
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages_fr.properties?rev=1529262&r1=1529261&r2=1529262&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages_fr.properties [ISO-8859-1] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages_fr.properties [ISO-8859-1] Fri Oct 4 19:08:18 2013
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+AlreadyRegistered_2 = Le {0} \u201c{1}\u201d est d\u00e9j\u00e0 inscrit dans le registre. La seconde instance sera ignor\u00e9e.
ChangedContainerCapacity_2 = Changement de la capacit\u00e9 du conteneur de {0} vers {1} \u00e9l\u00e9ments.
DiscardedExclusiveProperty_2 = La propri\u00e9t\u00e9 \u201c{0}\u201d a \u00e9t\u00e9 \u00e9cart\u00e9e en faveur de \u201c{1}\u201d, parce que ces deux propri\u00e9t\u00e9s sont mutuellement exclusives.
PropertyHiddenBy_2 = La propri\u00e9t\u00e9 \u201c{0}\u201d est masqu\u00e9e par \u201c{1}\u201d.
Modified: sis/trunk/core/sis-utility/src/test/java/org/apache/sis/test/TestUtilities.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/test/java/org/apache/sis/test/TestUtilities.java?rev=1529262&r1=1529261&r2=1529262&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/test/java/org/apache/sis/test/TestUtilities.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/test/java/org/apache/sis/test/TestUtilities.java [UTF-8] Fri Oct 4 19:08:18 2013
@@ -44,7 +44,7 @@ import static org.junit.Assert.*;
*
* @author Martin Desruisseaux (Geomatys)
* @since 0.3 (derived from geotk-3.16)
- * @version 0.3
+ * @version 0.4
* @module
*/
public final strictfp class TestUtilities extends Static {
@@ -90,6 +90,17 @@ public final strictfp class TestUtilitie
}
/**
+ * Prints and clear the current content of {@link TestCase#out}, regardless of whether
+ * {@link TestCase#verbose} is {@code true} or {@code false}. This method should rarely
+ * be needed.
+ *
+ * @since 0.4
+ */
+ public static void forceFlushOutput() {
+ TestCase.flushOutput();
+ }
+
+ /**
* If verbose output are enabled, prints the given title to {@link TestCase#out} in a box.
* This method is invoked for writing a clear visual separator between the verbose output
* of different test cases. This method does nothing if verbose output is not enabled,
Modified: sis/trunk/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java?rev=1529262&r1=1529261&r2=1529262&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java [UTF-8] Fri Oct 4 19:08:18 2013
@@ -58,9 +58,11 @@ import org.junit.BeforeClass;
org.apache.sis.math.StatisticsFormatTest.class,
org.apache.sis.internal.util.UtilitiesTest.class,
org.apache.sis.internal.util.NumericsTest.class,
+ org.apache.sis.internal.util.DoubleDoubleTest.class,
org.apache.sis.internal.jdk8.JDK8Test.class,
// Collections.
+ org.apache.sis.internal.util.CheckedArrayListTest.class,
org.apache.sis.internal.system.ReferenceQueueConsumerTest.class,
org.apache.sis.util.collection.WeakHashSetTest.class,
org.apache.sis.util.collection.WeakValueHashMapTest.class,