diff --git a/src/main/java/com/mitsuki/jmatrix/Matrix.java b/src/main/java/com/mitsuki/jmatrix/Matrix.java index b1861ac..9379935 100644 --- a/src/main/java/com/mitsuki/jmatrix/Matrix.java +++ b/src/main/java/com/mitsuki/jmatrix/Matrix.java @@ -20,12 +20,12 @@ package com.mitsuki.jmatrix; +import com.mitsuki.jmatrix.core.MatrixUtils; import com.mitsuki.jmatrix.exception.IllegalMatrixSizeException; import com.mitsuki.jmatrix.exception.InvalidIndexException; import com.mitsuki.jmatrix.exception.JMatrixBaseException; import com.mitsuki.jmatrix.exception.NullMatrixException; -import com.mitsuki.jmatrix.internal.JMatrixUtils; -import com.mitsuki.jmatrix.core.MatrixUtils; +import static com.mitsuki.jmatrix.exception.JMatrixBaseException.raise; import java.util.Arrays; import java.util.ArrayList; @@ -265,7 +265,7 @@ public Matrix(int rows, int cols) { } // Throw the exception if got one - if (cause != null) JMatrixUtils.raiseError(cause); + if (cause != null) raise((RuntimeException) cause); // Copy the sizes from input parameters this.ROWS = rows; @@ -327,7 +327,7 @@ public Matrix(int rows, int cols, double val) { } // Throw the exception if got one - if (cause != null) JMatrixUtils.raiseError(cause); + if (cause != null) raise((RuntimeException) cause); // Copy the sizes from input parameters this.ROWS = rows; @@ -390,7 +390,7 @@ public Matrix(int rows, int cols, double val) { public Matrix(double[ ][ ] arr) { // Raise the exception immediately if given array is null if (arr == null || arr.length == 0) { - JMatrixUtils.raiseError(new NullMatrixException( + raise(new NullMatrixException( "Given two-dimensional array is null. Please ensure the array has valid elements.")); } @@ -465,7 +465,7 @@ public void create(int rows, int cols) { } // Throw the exception if got one - if (cause != null) JMatrixUtils.raiseError(cause); + if (cause != null) raise((RuntimeException) cause); // Copy the sizes from input parameters this.ROWS = rows; @@ -493,7 +493,7 @@ public void create(int rows, int cols) { public void create(double[ ][ ] arr) { // Raise the exception immediately if given array is null if (arr == null || arr.length == 0) { - JMatrixUtils.raiseError(new NullMatrixException( + raise(new NullMatrixException( "Given two-dimensional array is null. Please ensure the array has valid elements.")); } @@ -555,7 +555,7 @@ public void create(double[ ][ ] arr) { public static Matrix identity(int n) { // Check for negative value on input argument if (n < 1) { - JMatrixUtils.raiseError(new IllegalMatrixSizeException( + raise(new IllegalMatrixSizeException( "Sizes of identity matrix cannot be lower than 1.")); } @@ -679,10 +679,10 @@ public Matrix addRow(double[] a) { */ public static Matrix addRow(Matrix m, double[] a) { if (MatrixUtils.isNullEntries(m)) { - JMatrixUtils.raiseError(new NullMatrixException( + raise(new NullMatrixException( "Matrix is null. Please ensure the matrix are initialized")); } else if (a == null || a.length == 0) { - JMatrixUtils.raiseError(new JMatrixBaseException(new NullPointerException( + raise(new JMatrixBaseException(new NullPointerException( "Given array is null or empty. Cannot append it into the matrix"))); } @@ -797,10 +797,10 @@ public Matrix addColumn(double[] a) { */ public static Matrix addColumn(Matrix m, double[] a) { if (MatrixUtils.isNullEntries(m)) { - JMatrixUtils.raiseError(new NullMatrixException( + raise(new NullMatrixException( "Matrix is null. Please ensure the matrix are initialized")); } else if (a == null || a.length == 0) { - JMatrixUtils.raiseError(new JMatrixBaseException(new NullPointerException( + raise(new JMatrixBaseException(new NullPointerException( "Given array is null or empty. Cannot append it into the matrix"))); } @@ -952,20 +952,20 @@ public static Matrix insertRow(Matrix m, int row, double[] a) { row += (row < 0) ? (mRows + 1) : 0; // Allow negative indexing if (MatrixUtils.isNullEntries(m)) { // Check for null matrix - JMatrixUtils.raiseError(new NullMatrixException( + raise(new NullMatrixException( "Matrix is null. Please ensure the matrix are initialized")); } else if (a == null || a.length == 0) { // Check for null or empty array - JMatrixUtils.raiseError(new JMatrixBaseException(new NullPointerException( + raise(new JMatrixBaseException(new NullPointerException( "Given array is null or empty. Cannot insert it into the matrix"))); } else if (row < 0 || row >= mRows) { // Check for the index is out of bounds - JMatrixUtils.raiseError(new InvalidIndexException( + raise(new InvalidIndexException( String.format("Given row index is out of range: %d", (row < 0) ? (row - mRows - 1) : row ) )); } else if (a.length < mRows) { // Check for the array length is less than matrix number of rows - JMatrixUtils.raiseError(new JMatrixBaseException( + raise(new JMatrixBaseException( new IllegalArgumentException(String.format( "The length of array is less than matrix row count: %d < %d", a.length, mRows @@ -1163,14 +1163,14 @@ public static Matrix insertColumn(Matrix m, int col, double[] a) { col += (col < 0) ? (mCols + 1) : 0; // Allow negative indexing if (col < 0 || col > mCols) { // Check for the index is out of bounds - JMatrixUtils.raiseError(new InvalidIndexException( + raise(new InvalidIndexException( String.format("Given column index is out of range: %d", (col < 0) ? (col - mCols - 1) : col ) )); } else if (a.length < mCols) { // Check for the array length is less than matrix number of rows - JMatrixUtils.raiseError(new JMatrixBaseException( + raise(new JMatrixBaseException( new IllegalArgumentException(String.format( "The length of array is less than matrix column count: %d < %d", a.length, mCols @@ -1263,7 +1263,7 @@ public Matrix dropRow(int row) { */ public static Matrix dropRow(Matrix m, int row) { if (MatrixUtils.isNullEntries(m)) { - JMatrixUtils.raiseError(new NullMatrixException( + raise(new NullMatrixException( "Matrix is null. Please ensure the matrix are initialized")); } @@ -1272,7 +1272,7 @@ public static Matrix dropRow(Matrix m, int row) { row += (row < 0) ? rows : 0; // Allow negative indexing if (row >= rows || row < 0) { - JMatrixUtils.raiseError(new InvalidIndexException( + raise(new InvalidIndexException( String.format("Given row index is out of range: %d", (row < 0) ? (row - rows) : row ) @@ -1371,7 +1371,7 @@ public Matrix dropColumn(int col) { */ public static Matrix dropColumn(Matrix m, int col) { if (MatrixUtils.isNullEntries(m)) { - JMatrixUtils.raiseError(new NullMatrixException( + raise(new NullMatrixException( "Matrix is null. Please ensure the matrix are initialized")); } @@ -1380,7 +1380,7 @@ public static Matrix dropColumn(Matrix m, int col) { col += (col < 0) ? cols : 0; // Allow negative indexing if (col >= cols || col < 0) { - JMatrixUtils.raiseError(new InvalidIndexException( + raise(new InvalidIndexException( String.format("Given column index is out of range: %d", (col < 0) ? (col - cols) : col ) @@ -1456,7 +1456,7 @@ public Matrix swapRows(int row1, int row2) { */ public static Matrix swapRows(Matrix m, int row1, int row2) { if (MatrixUtils.isNullEntries(m)) { - JMatrixUtils.raiseError(new NullMatrixException( + raise(new NullMatrixException( "Matrix is null. Please ensure the matrix are initialized.")); } @@ -1482,7 +1482,7 @@ public static Matrix swapRows(Matrix m, int row1, int row2) { } // Raise the error, if any - if (cause != null) JMatrixUtils.raiseError(cause); + if (cause != null) raise((RuntimeException) cause); // We prefer use the `System.arraycopy` method instead of `Arrays.copyOf` or // use a very slow method, *for-loop*. It because the `System.arraycopy` offers @@ -1553,7 +1553,7 @@ public Matrix swapColumns(int col1, int col2) { */ public static Matrix swapColumns(Matrix m, int col1, int col2) { if (MatrixUtils.isNullEntries(m)) { - JMatrixUtils.raiseError(new NullMatrixException( + raise(new NullMatrixException( "Matrix is null. Please ensure the matrix are initialized.")); } @@ -1578,7 +1578,7 @@ public static Matrix swapColumns(Matrix m, int col1, int col2) { } // Raise the error, if any - if (cause != null) JMatrixUtils.raiseError(cause); + if (cause != null) raise((RuntimeException) cause); for (int i = 0; i < entries.length; i++) { // Swap the elements of columns `col1` and `col2` directly without using @@ -1637,10 +1637,10 @@ else if (values.length < this.COLS) { try { throw new JMatrixBaseException(iae); } catch (final JMatrixBaseException jme) { - JMatrixUtils.raiseError(jme); + raise(jme); } } catch (final RuntimeException re) { - JMatrixUtils.raiseError(re); + raise(re); } // Iterate values list and fill elements of matrix array @@ -1685,7 +1685,7 @@ else if (this.index >= this.ROWS) { "Cannot add values anymore, Matrix is already full"); } } catch (final RuntimeException re) { - JMatrixUtils.raiseError(re); + raise(re); } // Creates list of repeated value @@ -1827,7 +1827,7 @@ else if (this.ROWS != m.ROWS || this.COLS != m.COLS) { } // Throw the exception if got one - if (cause != null) JMatrixUtils.raiseError(cause); + if (cause != null) raise((RuntimeException) cause); // Create new matrix for the result double[ ][ ] result = new double[this.ROWS][m.COLS]; @@ -1911,7 +1911,7 @@ else if (this.ROWS != arr.length || this.COLS != arr[0].length) { } // Throw the exception if got one - if (cause != null) JMatrixUtils.raiseError(cause); + if (cause != null) raise((RuntimeException) cause); // Create new matrix for the result double[ ][ ] result = new double[this.ROWS][arr[0].length]; @@ -1998,7 +1998,7 @@ else if (a.length != b.length || a[0].length != b[0].length) { } // Throw the exception if got one - if (cause != null) JMatrixUtils.raiseError(cause); + if (cause != null) raise((RuntimeException) cause); // Create a new array for the result double[ ][ ] result = new double[a.length][b[0].length]; @@ -2086,7 +2086,7 @@ else if (a.getSize()[0] != b.getSize()[0] || a.getSize()[1] != b.getSize()[1]) { } // Throw the exception if got one - if (cause != null) JMatrixUtils.raiseError(cause); + if (cause != null) raise((RuntimeException) cause); // Create new matrix object Matrix matrixRes = new Matrix(a.getSize()[0], b.getSize()[1]); @@ -2172,7 +2172,7 @@ else if (this.ROWS != m.ROWS || this.COLS != m.COLS) { } // Throw the exception if got one - if (cause != null) JMatrixUtils.raiseError(cause); + if (cause != null) raise((RuntimeException) cause); // Create new matrix for the result double[ ][ ] result = new double[this.ROWS][m.COLS]; @@ -2252,7 +2252,7 @@ else if (this.ROWS != arr.length || this.COLS != arr[0].length) { } // Throw the exception if got one - if (cause != null) JMatrixUtils.raiseError(cause); + if (cause != null) raise((RuntimeException) cause); // Create new matrix for the result double[ ][ ] result = new double[this.ROWS][arr[0].length]; @@ -2338,7 +2338,7 @@ else if (a.length != b.length || a[0].length != b[0].length) { ); } - if (cause != null) JMatrixUtils.raiseError(cause); + if (cause != null) raise((RuntimeException) cause); // Create a new matrix array double[ ][ ] result = new double[a.length][b[0].length]; @@ -2423,7 +2423,7 @@ else if (a.getSize()[0] != b.getSize()[0] || a.getSize()[1] != b.getSize()[1]) { ); } - if (cause != null) JMatrixUtils.raiseError(cause); + if (cause != null) raise((RuntimeException) cause); // Create new matrix object Matrix matrixRes = new Matrix(a.getSize()[0], b.getSize()[1]); @@ -2514,7 +2514,7 @@ else if (this.COLS != m.ROWS) { } // Throw the exception if got one - if (cause != null) JMatrixUtils.raiseError(cause); + if (cause != null) raise((RuntimeException) cause); // Create new matrix array double[ ][ ] result = new double[this.ROWS][m.COLS]; @@ -2600,7 +2600,7 @@ else if (this.COLS != a.length) { } // Throw the exception if got one - if (cause != null) JMatrixUtils.raiseError(cause); + if (cause != null) raise((RuntimeException) cause); // Create new matrix array double[ ][ ] result = new double[this.ROWS][a[0].length]; @@ -2686,7 +2686,7 @@ else if (a[0].length != b.length) { } // Throw the exception if got one - if (cause != null) JMatrixUtils.raiseError(cause); + if (cause != null) raise((RuntimeException) cause); double[ ][ ] result = new double[a.length][b[0].length]; @@ -2773,7 +2773,7 @@ else if (a.getSize()[1] != b.getSize()[0]) { } // Throw the exception if got one - if (cause != null) JMatrixUtils.raiseError(cause); + if (cause != null) raise((RuntimeException) cause); // Create new matrix object Matrix result = new Matrix(a.getSize()[0], b.getSize()[1]); @@ -2828,7 +2828,7 @@ else if (a.getSize()[1] != b.getSize()[0]) { public void mult(double x) { // Throw the exception immediately if this matrix has null entries if (this.ENTRIES == null) { - JMatrixUtils.raiseError(new NullMatrixException( + raise(new NullMatrixException( "This matrix is null. " + "Please ensure the matrix are initialized before performing scalar multiplication." )); @@ -2878,7 +2878,7 @@ public void mult(double x) { public static Matrix mult(Matrix m, double x) { // Throw the exception immediately if given matrix has null entries if (m == null || m.getEntries() == null) { - JMatrixUtils.raiseError(new NullMatrixException( + raise(new NullMatrixException( "Given matrix is null. " + "Please ensure the matrix are initialized before performing scalar multiplication." )); @@ -2955,7 +2955,7 @@ public static Matrix mult(Matrix m, double x) { public void transpose() { // Throw the exception immediately if this matrix has null entries if (this.ENTRIES == null) { - JMatrixUtils.raiseError(new NullMatrixException( + raise(new NullMatrixException( "This matrix is null. " + "Please ensure the matrix are initialized before performing transposition." )); @@ -2990,7 +2990,7 @@ public void transpose() { */ public static double[ ][ ] transpose(double[ ][ ] arr) { if (arr == null || arr.length == 0) { - JMatrixUtils.raiseError(new NullMatrixException( + raise(new NullMatrixException( "Given array is null. " + "Please ensure the array has valid elements before performing transposition." )); @@ -3060,7 +3060,7 @@ public void transpose() { public static Matrix transpose(Matrix m) { // Throw the exception immediately if the given matrix has null entries if (m == null || m.getEntries() == null) { - JMatrixUtils.raiseError(new NullMatrixException( + raise(new NullMatrixException( "Given matrix is null. " + "Please ensure the matrix are initialized before performing transposition." )); @@ -3177,7 +3177,7 @@ public double trace() { public static double trace(Matrix m) { // Raise an error if the matrix is not square if (!m.isSquare()) { - JMatrixUtils.raiseError(new IllegalMatrixSizeException( + raise(new IllegalMatrixSizeException( "Matrix is non-square type. " + "Please ensure the matrix has the same number of rows and columns." )); @@ -3232,7 +3232,7 @@ public static double trace(Matrix m) { */ public static double trace(double[ ][ ] arr) { if (!Matrix.isSquare(arr)) { - JMatrixUtils.raiseError(new IllegalMatrixSizeException( + raise(new IllegalMatrixSizeException( "Array is non-square type. " + "Please ensure the array has the same number of rows and columns." )); @@ -3367,12 +3367,12 @@ public Matrix minorMatrix(int row, int col) { */ public static Matrix minorMatrix(Matrix m, int row, int col) { if (MatrixUtils.isNullEntries(m)) { - JMatrixUtils.raiseError(new NullMatrixException( + raise(new NullMatrixException( "Matrix is null. Please ensure the matrix are initialized")); } if (!m.isSquare()) { - JMatrixUtils.raiseError(new IllegalMatrixSizeException( + raise(new IllegalMatrixSizeException( "Matrix is not square. Please ensure the matrix have the same rows" + " and columns size" )); @@ -3437,7 +3437,7 @@ public boolean isSquare() { */ public static boolean isSquare(double[ ][ ] arr) { if (arr == null || arr.length == 0) { - JMatrixUtils.raiseError(new NullMatrixException( + raise(new NullMatrixException( "Given array is null. Please ensure the array has valid elements.")); } @@ -3464,7 +3464,7 @@ public static boolean isSquare(double[ ][ ] arr) { */ public static boolean isSquare(Matrix m) { if (m == null || m.getEntries() == null) { - JMatrixUtils.raiseError(new NullMatrixException( + raise(new NullMatrixException( "Matrix is null. Please ensure the matrix are initialized.")); } @@ -3517,7 +3517,7 @@ public boolean isDiagonal() { */ public static boolean isDiagonal(Matrix m) { if (!m.isSquare()) { - JMatrixUtils.raiseError(new IllegalMatrixSizeException( + raise(new IllegalMatrixSizeException( "Matrix is non-square type. " + "Please ensure the matrix has the same number of rows and columns." )); @@ -3554,7 +3554,7 @@ public static boolean isDiagonal(Matrix m) { */ public static boolean isDiagonal(double[ ][ ] arr) { if (!Matrix.isSquare(arr)) { - JMatrixUtils.raiseError(new IllegalMatrixSizeException( + raise(new IllegalMatrixSizeException( "Given array is non-square type. " + "Please ensure the array has the same number of rows and columns." )); @@ -3667,14 +3667,14 @@ public boolean isLowerTriangular() { */ public static boolean isLowerTriangular(Matrix m) { if (MatrixUtils.isNullEntries(m)) { - JMatrixUtils.raiseError(new NullMatrixException( + raise(new NullMatrixException( "Matrix is null. Please ensure the matrix have been initialized.") ); } // The matrix must be square else if (!m.isSquare()) { - JMatrixUtils.raiseError(new IllegalMatrixSizeException( + raise(new IllegalMatrixSizeException( "Matrix is non-square type. " + "Please ensure the matrix has the same number of rows and columns." )); @@ -3736,14 +3736,14 @@ else if (!m.isSquare()) { */ public static boolean isLowerTriangular(double[ ][ ] arr) { if (arr == null || arr.length == 0) { - JMatrixUtils.raiseError(new NullMatrixException( + raise(new NullMatrixException( "Array is null. Please ensure the array has valid elements.") ); } // The two-dimensional array must be square else if (!Matrix.isSquare(arr)) { - JMatrixUtils.raiseError(new IllegalMatrixSizeException( + raise(new IllegalMatrixSizeException( "Array is non-square type. " + "Please ensure the array has the same number of rows and columns." )); @@ -3856,14 +3856,14 @@ public boolean isUpperTriangular() { */ public static boolean isUpperTriangular(Matrix m) { if (MatrixUtils.isNullEntries(m)) { - JMatrixUtils.raiseError(new NullMatrixException( + raise(new NullMatrixException( "Matrix is null. Please ensure the matrix have been initialized.") ); } // The matrix must be square else if (!m.isSquare()) { - JMatrixUtils.raiseError(new IllegalMatrixSizeException( + raise(new IllegalMatrixSizeException( "Matrix is non-square type. " + "Please ensure the matrix has the same number of rows and columns." )); @@ -3925,14 +3925,14 @@ else if (!m.isSquare()) { */ public static boolean isUpperTriangular(double[ ][ ] arr) { if (arr == null || arr.length == 0) { - JMatrixUtils.raiseError(new NullMatrixException( + raise(new NullMatrixException( "Array is null. Please ensure the array has valid elements.") ); } // The matrix must be square else if (!Matrix.isSquare(arr)) { - JMatrixUtils.raiseError(new IllegalMatrixSizeException( + raise(new IllegalMatrixSizeException( "Array is non-square type. " + "Please ensure the array has the same number of rows and columns." )); @@ -4058,7 +4058,7 @@ public static boolean isSparse(Matrix m) { int numberNonZero = 0; // To hold the total number of non-zero entries if (MatrixUtils.isNullEntries(m)) { - JMatrixUtils.raiseError(new NullMatrixException( + raise(new NullMatrixException( "Matrix is null. Please ensure the matrix have been initialized.") ); } @@ -4129,7 +4129,7 @@ public static boolean isSparse(double[ ][ ] a) { int numberNonZero = 0; // To hold the total number of non-zero entries if (a == null || a.length == 0) { - JMatrixUtils.raiseError(new NullMatrixException( + raise(new NullMatrixException( "Array is null. Please ensure the array has valid elements.") ); } @@ -4204,12 +4204,12 @@ public boolean isIdentity() { */ public static boolean isIdentity(Matrix m) { if (MatrixUtils.isNullEntries(m)) { // Check for uninitialized matrix - JMatrixUtils.raiseError(new NullMatrixException( + raise(new NullMatrixException( "Matrix is null. Please ensure the matrix have been initialized.")); } // ** no else-if after throw if (!m.isSquare()) { // Check for non-square matrix - JMatrixUtils.raiseError(new IllegalMatrixSizeException( + raise(new IllegalMatrixSizeException( "Matrix is not square. " + "Please ensure the matrix has the same number of rows and columns." )); @@ -4260,12 +4260,12 @@ public static boolean isIdentity(Matrix m) { */ public static boolean isIdentity(double[][] arr) { if (arr == null || arr.length == 0) { // Check for null array - JMatrixUtils.raiseError(new NullMatrixException( + raise(new NullMatrixException( "Array is null. Please ensure the array have been initialized.")); } // ** no else-if after throw if (!Matrix.isSquare(arr)) { // Check for non-square array - JMatrixUtils.raiseError(new IllegalMatrixSizeException( + raise(new IllegalMatrixSizeException( "Array is not square. " + "Please ensure the array has the same number of rows and columns." )); @@ -4325,7 +4325,7 @@ public Matrix copy() { "Matrix is null. Please ensure the matrix are initialized."); } } catch (final NullMatrixException nme) { - JMatrixUtils.raiseError(nme); + raise(nme); } // Create new and copy the matrix @@ -4416,7 +4416,7 @@ else if (index > this.ROWS - 1) { "Given index is too larger than number of rows."); } - if (cause != null) JMatrixUtils.raiseError(cause); + if (cause != null) raise((RuntimeException) cause); this.selectedIndex = index; this.hasSelect = true; @@ -4499,7 +4499,7 @@ else if (!this.hasSelect) { } // Throw the exception if got one - if (cause != null) JMatrixUtils.raiseError(cause); + if (cause != null) raise((RuntimeException) cause); // Change values of matrix column with values from argument parameter for (int i = 0; i < this.COLS; i++) { @@ -4561,7 +4561,7 @@ public void change(double value) { // Check if the user have not select any index row // If user have not then it will immediately raise the exception if (!this.hasSelect) { - JMatrixUtils.raiseError(new InvalidIndexException( + raise(new InvalidIndexException( "Selected index is null. " + "Please ensure you have already called \"select(int)\" method." )); @@ -4600,7 +4600,7 @@ public void change(double value) { */ public void clear() { if (this.ENTRIES == null) { - JMatrixUtils.raiseError(new NullMatrixException( + raise(new NullMatrixException( "Matrix is null. Please ensure the matrix have been initialized.")); } @@ -4630,7 +4630,7 @@ public void clear() { */ public void sort() { if (this.ENTRIES == null) { - JMatrixUtils.raiseError(new NullMatrixException( + raise(new NullMatrixException( "This matrix is null. Please ensure the matrix are initialized.")); } @@ -4660,7 +4660,7 @@ public void sort() { */ public static void sort(double[ ][ ] arr) { if (arr == null || arr.length == 0) { - JMatrixUtils.raiseError(new NullMatrixException( + raise(new NullMatrixException( "Given array is null. Please ensure the array has valid elements.")); } @@ -4687,7 +4687,7 @@ public static void sort(double[ ][ ] arr) { public static Matrix sort(Matrix m) { // Check for matrix with null entries if (m == null || m.getEntries() == null) { - JMatrixUtils.raiseError(new NullMatrixException( + raise(new NullMatrixException( "Given matrix is null. Please ensure the matrix are initialized.")); } @@ -4994,7 +4994,7 @@ public static double getEntry(Matrix m, int row, int col) { } // Throw the exception if got one - if (cause != null) JMatrixUtils.raiseError(cause); + if (cause != null) raise((RuntimeException) cause); return m.ENTRIES[row][col]; } @@ -5061,7 +5061,7 @@ public static double getEntry(Matrix m, int row, int col) { */ public List> getReadOnlyEntries() { if (MatrixUtils.isNullEntries(this)) { - JMatrixUtils.raiseError(new NullMatrixException( + raise(new NullMatrixException( "Matrix is null. Please ensure the matrix are initialized.")); } @@ -5153,7 +5153,7 @@ else if (index > this.ROWS - 1) { } // Throw the exception if got one - if (cause != null) JMatrixUtils.raiseError(cause); + if (cause != null) raise((RuntimeException) cause); System.out.println(Arrays.toString(this.ENTRIES[index])); } else { @@ -5226,7 +5226,7 @@ final public static void display(double[ ][ ] arr, int index) { } // Throw the exception if got one - if (cause != null) JMatrixUtils.raiseError(cause); + if (cause != null) raise((RuntimeException) cause); System.out.println(Arrays.toString(arr[index])); } diff --git a/src/main/java/com/mitsuki/jmatrix/enums/JMErrorCode.java b/src/main/java/com/mitsuki/jmatrix/enums/JMErrorCode.java new file mode 100644 index 0000000..7514668 --- /dev/null +++ b/src/main/java/com/mitsuki/jmatrix/enums/JMErrorCode.java @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2023-2024 Ryuu Mitsuki + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.mitsuki.jmatrix.enums; + +import com.mitsuki.jmatrix.Matrix; +import com.mitsuki.jmatrix.exception.*; + +/** + * The {@code JMErrorCode} enum represents error codes for various error conditions + * that can occur within a JMatrix library. Each error code is associated with + * an integer error number, a string representation of the error code, and a descriptive + * error message. + * + *

This enum provides methods to retrieve the error number, error code, and error message. + * Additionally, it provides a static method to convert an error number or a string + * representation of the error code back to its corresponding {@code JMErrorCode} enum value, + * it is declared as {@link #valueOf(Object)}. The method does not throws an error if the type + * of value is unrecognized, instead it returns {@code null}. + * + *

Example usage: + *

 
+ *   JMErrorCode errorCode = JMErrorCode.INVIDX;
+ *   int errno = errorCode.getErrno();        // 201
+ *   String errcode = errorCode.getCode();    // INVIDX
+ *   String errmsg = errorCode.getMessage();  // Given index is out bounds
+ * 
+ * + *

Get the error code from a thrown exception: + *

 
+ *   try {
+ *      Matrix m = new Matrix();
+ *      m = Matrix.mult(m, 5);
+ *   } catch (Exception e) {
+ *      if (e instanceof JMatrixBaseException) {
+ *          e = (JMatrixBaseException) e;
+ *          String errcode = e.getErrorCode().getCode();  // NULLMT
+ *      }
+ *   }
+ * 
+ * + * @since 1.5.0 + * @version 1.0, 05 June 2024 + * @author + * Ryuu Mitsuki + * @license + * Apache License 2.0 + * @see java.lang.Enum + */ +public enum JMErrorCode { + /** + * Error code indicating that the given index is out of bounds. + * + *

This error code is useful to indicates that user gives an index + * either of row or column (which from methods that indexing-related, + * such as {@link Matrix#insertRow(int, double[])}, {@link + * Matrix#dropColumn(int)}, and {@link Matrix#display(int)}). + * + *

Error number: {@code 201} + *

Related exception: {@link InvalidIndexException} + */ + INVIDX( 0xD3 ^ (0xD + 0xD), "Given index is out of bounds" ), + /** + * Error code indicating that the matrix has an invalid type. + * + *

This error code will be in the {@link IllegalMatrixSizeException} exception + * which related to invalid type of matrix. For example, user attempting to + * calculate trace with a {@code 5x3} matrix, which is an illegal attemption + * because trace calculation are only for square matrices. + * + *

Error number: {@code 202} + *

Related exception: {@link IllegalMatrixSizeException} + */ + INVTYP( 0xD3 ^ (0xD + 0xC), "Matrix has invalid type of matrix" ), + /** + * Error code indicating that the matrix is {@code null} or the matrix + * entries is {@code null}. + * + *

A matrix with {@code null} entries can be constructed using + * {@link Matrix#Matrix()} constructor, which is not require any arguments. + * Thus, the returned matrix is a matrix with a {@code null} entries (in other + * words, an uninitialized matrix). + * + *

Error number: {@code 203} + *

Related exception: {@link NullMatrixException} + */ + NULLMT( 0xD3 ^ (0xD + 0xB), "Matrix is null" ), + /** + * Error code indicating an unknown error. + * + *

This error code can be made by the {@link JMatrixBaseException} class + * when failed to evaluate the given error number (errno) or the cause exception + * is not an instance of that class while trying to get the error code from the + * given cause exception, and ended up constructs with this error code. + * + *

Error number: {@code 400} + *

Related exception: {@link JMatrixBaseException} + */ + UNKERR( ((0xD3 << 1) - (0xF & 0xF) - 0b111), "Unknown error" ); + + /** The error nuumber stored for later retrieval by {@link #getErrno()} */ + private int errno = 0x00; + /** The error code stored for later retrieval by {@link #getCode()} */ + private String code = null; + /** The error message stored for later retrieval by {@link #getMessage()} */ + private String message = null; + + /** + * Constructs a {@code JMErrorCode} with the specified error number and detail message. + * + * @param errno The error number associated with this error code. + * @param message The descriptive error message associated with this error code. + * + * @since 1.5.0 + */ + JMErrorCode(int errno, String message) { + this.code = super.toString(); + this.errno = errno; + this.message = message; + } + + /** + * Returns the error number associated with this error code. + * + * @return The error number. + * + * @since 1.5.0 + * @see #getErrnoStr() + */ + public int getErrno() { + return this.errno; + } + + /** + * Returns the error number as a string in the format {@code JM###}. + * + * @return The error number as a string, prefixed with {@code JM}. + * + * @since 1.5.0 + * @see #getErrno() + */ + public String getErrnoStr() { + return "JM" + this.errno; // Return: JM### + } + + /** + * Returns the string representation of this error code. + * + * @return The error code. + * + * @since 1.5.0 + */ + public String getCode() { + return this.code; + } + + /** + * Returns the descriptive error message associated with this error code. + * + * @return The descriptive error message. + * + * @since 1.5.0 + */ + public String getMessage() { + return this.message; + } + + /** + * {@inheritDoc} + * + * @since 1.5.0 + */ + @Override + public String toString() { + return String.format("%s[%s]", this.code, this.errno); + } + + /** + * Returns the {@code JMErrorCode} corresponding to the specified value. + * + *

The value can be either an integer (error number) or a string (error code). + * If the type of {@code x} is not recognized, this method returns {@code null}. + * + * @param The type of value, which can be either {@link Integer} or {@link String}. + * @param x The value to be evaluated. + * + * @return The corresponding {@code JMErrorCode}, or {@code null} if the type of + * {@code x} is not known. + * + * @throws IllegalArgumentException If this enum has no constant with the specified + * name (with string representation). + * + * @since 1.5.0 + * @see Enum#valueOf(Class, String) + */ + public static JMErrorCode valueOf(T x) { + if (x instanceof Integer) { + for (JMErrorCode ec : JMErrorCode.values()) { + if ((Integer) x == ec.getErrno()) return ec; + } + } else if (x instanceof String) { + // This might throws an `IllegalArgumentException` + return JMErrorCode.valueOf((String) x); + } + //* No error being thrown if got unknown type, but return `null` + return null; + } +} diff --git a/src/main/java/com/mitsuki/jmatrix/exception/IllegalMatrixSizeException.java b/src/main/java/com/mitsuki/jmatrix/exception/IllegalMatrixSizeException.java index 148140a..07853f8 100644 --- a/src/main/java/com/mitsuki/jmatrix/exception/IllegalMatrixSizeException.java +++ b/src/main/java/com/mitsuki/jmatrix/exception/IllegalMatrixSizeException.java @@ -2,7 +2,8 @@ /* -- IllegalMatrixSizeException -- */ // :: ---------------------------- :: // -/* Copyright (c) 2023 Ryuu Mitsuki +/* + * Copyright (c) 2023-2024 Ryuu Mitsuki * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +20,8 @@ package com.mitsuki.jmatrix.exception; +import com.mitsuki.jmatrix.enums.JMErrorCode; + /** * Thrown when attempting to perform matrix operations with matrices that do not * conform to the sizes requirements of the operation. @@ -29,14 +32,13 @@ *

Type: Unchecked exception

* * @since 0.1.0 - * @version 1.2, 18 July 2023 + * @version 1.3, 10 June 2024 * @author * Ryuu Mitsuki * @license * Apache License 2.0 */ -public class IllegalMatrixSizeException extends JMatrixBaseException -{ +public class IllegalMatrixSizeException extends JMatrixBaseException { /** * Stores the serial version number of this class for deserialization to @@ -45,14 +47,10 @@ public class IllegalMatrixSizeException extends JMatrixBaseException * * @see java.io.Serializable */ - private static final long serialVersionUID = 30_796_526_592L; + private static final long serialVersionUID = 43_003_192_023_202L; - /** - * Stores a string represents the detail message of this exception. - * - * @see #getMessage() - */ - private String message = null; + /** Stores the default errno of this exception based on {@link JMErrorCode}. */ + private static int defaultErrno = JMErrorCode.INVTYP.getErrno(); /** * Constructs a new {@code IllegalMatrixSizeException} with no detail message. @@ -60,7 +58,7 @@ public class IllegalMatrixSizeException extends JMatrixBaseException * @since 0.1.0 */ public IllegalMatrixSizeException() { - super(); + super(defaultErrno, null); } /** @@ -71,44 +69,118 @@ public IllegalMatrixSizeException() { * @since 0.1.0 */ public IllegalMatrixSizeException(String message) { - super(message); - this.message = message; + super(defaultErrno, message); } /** - * Constructs a new {@code IllegalMatrixSizeException} with the specified cause - * and a detail message of the cause. + * Constructs a new {@code IllegalMatrixSizeException} with the specified error + * number and the detailed message. + * + *

The given errno will replace the default errno of this exception. + * + * @param errno The error number. + * @param message The detail message and will be saved for later retrieval + * by the {@link #getMessage()} method. + * + * @since 1.5.0 + * @see #getMessage() + * @see #getErrorCode() + */ + public IllegalMatrixSizeException(int errno, String message) { + super(errno, message); + defaultErrno = errno; + } + + /** + * Constructs a new {@code IllegalMatrixSizeException} with the specified cause. * - * @param cause the cause of this exception. + *

This constructor allows for exception chaining, where the new exception + * is caused by an existing throwable. This is useful for wrapping lower-level + * exceptions in higher-level exceptions, providing more context about the error + * that occurred. * - * @since 0.1.0 + * @param cause The cause of this exception. A {@code null} value is permitted + * and indicates that the cause is nonexistent or unknown. + * + * @since 0.1.0 */ public IllegalMatrixSizeException(Throwable cause) { super(cause); - this.message = cause.getMessage(); } /** - * Returns the detail message of this exception. + * Constructs a new {@code IllegalMatrixSizeException} with the specified cause + * and a detailed message for later retrieval by {@link #getMessage()}. + * + *

This constructor allows for exception chaining, where the new exception + * is caused by an existing throwable. This is useful for wrapping lower-level + * exceptions in higher-level exceptions, providing more context about the error + * that occurred. + * + * @param s The descriptive message. + * @param cause The cause of this exception. A {@code null} value is permitted + * and indicates that the cause is nonexistent or unknown. + * + * @since 1.5.0 + */ + public IllegalMatrixSizeException(String s, Throwable cause) { + super(s, cause); + } + + /** + * Constructs a new {@code IllegalMatrixSizeException} with the specified detail message, + * cause, suppression enabled or disabled, and writable stack trace enabled or disabled. * - * @return the detail message. + * @param s The detail message (which is saved for later retrieval + * by the {@link #getMessage()} method) + * @param cause The cause (which is saved for later retrieval by the + * {@link #getCause()} method). + * A {@code null} value is permitted, and indicates that + * the cause is nonexistent or unknown. + * @param enableSuppression Whether or not suppression is enabled or disabled. + * @param writableStackTrace Whether or not the stack trace should be writable. + * + * @since 1.5.0 + */ + protected IllegalMatrixSizeException(String s, Throwable cause, + boolean enableSuppression, + boolean writableStackTrace) { + super(s, cause, enableSuppression, writableStackTrace); + } + + /** + * {@inheritDoc} * * @since 0.1.0 */ @Override public String getMessage() { - return this.message; + return super.getMessage(); } /** - * Returns a string representation of this exception, including the class name and the detail message. + * {@inheritDoc} * - * @return a string representation of this exception. + * @since 1.5.0 + */ + @Override + public JMErrorCode getErrorCode() { + return JMErrorCode.valueOf(defaultErrno); + } + + /** + * {@inheritDoc} * * @since 0.1.0 */ @Override public String toString() { - return String.format("%s: %s", this.getClass().getName(), this.message); + JMErrorCode ec = this.getErrorCode(); + String errmsg = this.getMessage(); + return String.format(this.ERR_MSG_WITH_CODE_FORMAT, + this.getClass().getName(), + ec.getCode(), + (errmsg != null) ? errmsg : ec.getMessage() + ); } } diff --git a/src/main/java/com/mitsuki/jmatrix/exception/InvalidIndexException.java b/src/main/java/com/mitsuki/jmatrix/exception/InvalidIndexException.java index 337554d..c3f0840 100644 --- a/src/main/java/com/mitsuki/jmatrix/exception/InvalidIndexException.java +++ b/src/main/java/com/mitsuki/jmatrix/exception/InvalidIndexException.java @@ -2,7 +2,8 @@ /* -- InvalidIndexException -- */ // :: ----------------------- :: // -/* Copyright (c) 2023 Ryuu Mitsuki +/* + * Copyright (c) 2023-2024 Ryuu Mitsuki * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +20,8 @@ package com.mitsuki.jmatrix.exception; +import com.mitsuki.jmatrix.enums.JMErrorCode; + /** * Thrown when attempting to retrieve an entry at specified indices of a matrix, but the given indices are out of range. * This exception can also be thrown when providing an invalid (out of range) @@ -31,14 +34,13 @@ *

Type: Unchecked exception

* * @since 0.1.0 - * @version 1.2, 18 July 2023 + * @version 1.3, 10 June 2024 * @author * Ryuu Mitsuki * @license * Apache License 2.0 */ -public class InvalidIndexException extends JMatrixBaseException -{ +public class InvalidIndexException extends JMatrixBaseException { /** * Stores the serial version number of this class for deserialization to @@ -47,14 +49,10 @@ public class InvalidIndexException extends JMatrixBaseException * * @see java.io.Serializable */ - private static final long serialVersionUID = 10_585_989_792L; + private static final long serialVersionUID = 43_003_192_023_201L; - /** - * Stores a string represents the detail message of this exception. - * - * @see #getMessage() - */ - private String message = null; + /** Stores the default errno of this exception based on {@link JMErrorCode}. */ + private static int defaultErrno = JMErrorCode.INVIDX.getErrno(); /** * Constructs a new {@code InvalidIndexException} with no detail message. @@ -62,7 +60,7 @@ public class InvalidIndexException extends JMatrixBaseException * @since 0.2.0 */ public InvalidIndexException() { - super(); + super(defaultErrno, null); } /** @@ -73,44 +71,118 @@ public InvalidIndexException() { * @since 0.2.0 */ public InvalidIndexException(String message) { - super(message); - this.message = message; + super(defaultErrno, message); } /** - * Constructs a new {@code InvalidIndexException} with the specified cause - * and a detail message of the cause. + * Constructs a new {@code InvalidIndexException} with the specified error + * number and the detailed message. + * + *

The given errno will replace the default errno of this exception. + * + * @param errno The error number. + * @param s The detail message and will be saved for later retrieval + * by the {@link #getMessage()} method. + * + * @since 1.5.0 + * @see #getMessage() + * @see #getErrorCode() + */ + public InvalidIndexException(int errno, String s) { + super(errno, s); + defaultErrno = errno; + } + + /** + * Constructs a new {@code InvalidIndexException} with the specified cause. * - * @param cause the cause of this exception. + *

This constructor allows for exception chaining, where the new exception + * is caused by an existing throwable. This is useful for wrapping lower-level + * exceptions in higher-level exceptions, providing more context about the error + * that occurred. * - * @since 0.2.0 + * @param cause The cause of this exception. A {@code null} value is permitted + * and indicates that the cause is nonexistent or unknown. + * + * @since 0.2.0 */ public InvalidIndexException(Throwable cause) { super(cause); - this.message = cause.getMessage(); } /** - * Returns the detail message of this exception. + * Constructs a new {@code InvalidIndexException} with the specified cause + * and a detailed message for later retrieval by {@link #getMessage()}. + * + *

This constructor allows for exception chaining, where the new exception + * is caused by an existing throwable. This is useful for wrapping lower-level + * exceptions in higher-level exceptions, providing more context about the error + * that occurred. + * + * @param s The descriptive message. + * @param cause The cause of this exception. A {@code null} value is permitted + * and indicates that the cause is nonexistent or unknown. + * + * @since 1.5.0 + */ + public InvalidIndexException(String s, Throwable cause) { + super(s, cause); + } + + /** + * Constructs a new {@code InvalidIndexException} with the specified detail message, + * cause, suppression enabled or disabled, and writable stack trace enabled or disabled. * - * @return the detail message. + * @param s The detail message (which is saved for later retrieval + * by the {@link #getMessage()} method) + * @param cause The cause (which is saved for later retrieval by the + * {@link #getCause()} method). + * A {@code null} value is permitted, and indicates that + * the cause is nonexistent or unknown. + * @param enableSuppression Whether or not suppression is enabled or disabled. + * @param writableStackTrace Whether or not the stack trace should be writable. + * + * @since 1.5.0 + */ + protected InvalidIndexException(String s, Throwable cause, + boolean enableSuppression, + boolean writableStackTrace) { + super(s, cause, enableSuppression, writableStackTrace); + } + + /** + * {@inheritDoc} * * @since 0.2.0 */ @Override public String getMessage() { - return this.message; + return super.getMessage(); } /** - * Returns a string representation of this exception, including the class name and the detail message. + * {@inheritDoc} * - * @return a string representation of this exception. + * @since 1.5.0 + */ + @Override + public JMErrorCode getErrorCode() { + return JMErrorCode.valueOf(defaultErrno); + } + + /** + * {@inheritDoc} * * @since 0.2.0 */ @Override public String toString() { - return String.format("%s: %s", this.getClass().getName(), this.message); + JMErrorCode ec = this.getErrorCode(); + String errmsg = this.getMessage(); + return String.format(this.ERR_MSG_WITH_CODE_FORMAT, + this.getClass().getName(), + ec.getCode(), + (errmsg != null) ? errmsg : ec.getMessage() + ); } } diff --git a/src/main/java/com/mitsuki/jmatrix/exception/JMatrixBaseException.java b/src/main/java/com/mitsuki/jmatrix/exception/JMatrixBaseException.java index 3ced942..8d27676 100644 --- a/src/main/java/com/mitsuki/jmatrix/exception/JMatrixBaseException.java +++ b/src/main/java/com/mitsuki/jmatrix/exception/JMatrixBaseException.java @@ -2,7 +2,8 @@ /* -- JMatrixBaseException -- */ // :: -------------------------- :: // -/* Copyright (c) 2023 Ryuu Mitsuki +/* + * Copyright (c) 2023-2024 Ryuu Mitsuki * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,67 +20,279 @@ package com.mitsuki.jmatrix.exception; +import com.mitsuki.jmatrix.Matrix; +import com.mitsuki.jmatrix.core.MatrixUtils; +import com.mitsuki.jmatrix.enums.JMErrorCode; + +import java.io.PrintStream; import java.lang.RuntimeException; +import java.lang.SecurityException; /** - * Base exception class for all JMatrix exceptions. This exception and its subclasses are unchecked exceptions, - * which means they can be thrown during runtime. + * Base exception class for all JMatrix exceptions. This exception and its subclasses + * are unchecked exceptions, which means they can be thrown during runtime without being + * explicitly caught or declared. + * + *

The class {@code JMatrixBaseException} serves as a foundational exception that can be + * used in a matrix-related context. It provides a consistent interface for handling errors + * and can wrap other exceptions, allowing for a more flexible and unified error-handling + * strategy across the JMatrix library. * *

Type: Unchecked exception

* + *

As of version 1.5.0, a new capability has been introduced to configure the behavior + * of auto-raised exceptions either using an environment variable or system property. This + * feature allows applications to determine whether exceptions should be automatically + * raised and cause the application to exit, or if they should be thrown and handled + * via traditional try-catch blocks. This flexibility is crucial for scenarios where + * different environments or phases of development might require different error-handling + * approaches. + * + *

Configure Auto-raise Behavior: + * + *

The behavior is controlled by setting either the system property {@value #raiseConfName} + * or the environment variable {@value #raiseConfEnvName}. The configuration options are + * as follows: + * + *

+ * + *

If the auto-raise configuration is set using both environment variables + * and system properties, the system property will take precedence. + * + *

This enhancement offers greater control over exception handling, making the + * JMatrix library more adaptable to various use cases and environments. It + * ensures that developers can choose the most appropriate error-handling strategy + * based on their specific needs. + * + *

It is RECOMMENDED to set the configuration using system property. + * Here is an example usage to set the {@value #raiseConfName} configuration using + * system property from command-line. + *

 
+ *   $ java -D{@value #raiseConfName}=manual -cp /path/to/jmatrix-<VERSION>.jar Foo.java
+ * 
+ * + *

And this is an example usage to set the {@value #raiseConfName} configuration at runtime. + *

 
+ *   // Set the environment variable to configure behavior
+ *   System.setProperty({@value #raiseConfName}, "manual");
+ *   try {
+ *      // Some matrix-related operations that might throw
+ *      // JMatrixBaseException or its subclasses
+ *   } catch (JMatrixBaseException e) {
+ *      // Handle exception
+ *   }
+ * 
+ * * @since 1.0.0b.1 - * @version 1.2, 18 July 2023 + * @version 1.3, 10 June 2024 * @author * Ryuu Mitsuki * @license * Apache License 2.0 */ -public class JMatrixBaseException extends RuntimeException -{ +public class JMatrixBaseException extends RuntimeException { /** * Stores the serial version number of this class for deserialization to * verify that the sender and receiver of a serialized object have loaded classes * for that object that are compatible with respect to serialization. * - * @see java.io.Serializable + * @serial + * @see java.io.Serializable */ - private static final long serialVersionUID = 8_294_400_000L; + private static final long serialVersionUID = 43_003_192_023_400L; /** - * Stores the stack trace elements for this exception. + * A string represents the detail message of this exception. * - * @see StackTraceElement - * @see Throwable#getStackTrace() + * @serial + * @see #getMessage() */ - private StackTraceElement[ ] stackTraceElements = null; + private String message = null; /** - * Stores the stack trace elements for the causing exception. + * Stores the corresponding {@link JMErrorCode} for later retrieval + * by {@link #getErrorCode()}. * - * @see StackTraceElement - * @see Throwable#getStackTrace() + * @serial + * @see #getErrorCode() */ - private StackTraceElement[ ] causedStackTraceElements = null; + private JMErrorCode errcode = null; + + /** An error message format without the error code. */ + protected final String ERR_MSG_FORMAT = "%s: %s"; + /** An error message format with the error code. */ + protected final String ERR_MSG_WITH_CODE_FORMAT = "%s <%s>: %s"; + + /** A stack trace format for this throwable's stack traces. */ + private final String STACK_TRACE_FORMAT = "\tat \"%s.%s\" -> \"%s\": line %d\n"; + /** A stack trace format for this throwable's stack traces. */ + private final String EXCEPTION_INFO_FORMAT = + "\n[EXCEPTION INFO]\nType: %s\nCode: %s\nMessage: %s\n"; + + /** Caption for labelling this exception. */ + private final String MSG_CAPTION = "/!\\ EXCEPTION"; + /** Caption for labelling causative exception stack traces. */ + private final String CAUSE_CAPTION = "/!\\ CAUSED BY"; + + /** A property name for the auto-raise configuration. */ + private static final String raiseConfName = "jm.autoraise"; + /** An environment variable name for the auto-raise configuration. */ + private static final String raiseConfEnvName = "jm_autoraise"; /** - * Stores the string representation of the causing exception. + * The value of auto-raise configuration. + * + *

The value can be one of the following: + *

* - * @see Throwable#toString() + * Fallback to {@code auto} if both the {@value #raiseConfName} (in system + * properties) and {@value #raiseConfEnvName} (in environment variables) is not + * defined or set to an empty string. + * + * @see #getRaiseConfig() + * @see #getRaiseConfigFromSystemProps() */ - private String strCause = null; + private static String raiseConf = getRaiseConfig(); /** - * Stores a string represents the detail message of this exception. + * Retrieves the value of the {@value #raiseConfName} from the system property, + * if not set, then it tries to retrieves from environment variables named + * {@value #raiseConfEnvName}. + * + *

This method checks the value of the {@value #raiseConfName} (from system + * properties) or {@value #raiseConfEnvName} (from environment variables) and sets + * the configuration accordingly. The configuration will be set to {@code auto} + * if the value is equal to one of these known values (case-insensitive): + *

+ * + *

The configuration will be set to {@code manual} if the value is + * equal to one of these known values: + *

+ * + *

If the auto-raise configuration from the system properties is not set or + * is empty, it tries to retrieve the {@value #raiseConfEnvName} from environment + * variables. If not set or is an empty string too, set the auto-raise configuration + * to {@code auto}. + * + *

If the {@value #raiseConfName} configuration is set using both environment + * variables and system properties, the system property will take precedence. + * So, it is recommended to set the auto-raise configuration using system property. + * + * @implNote This method is synchronized to ensure thread safety. + * + * @return The configuration value, either {@code auto} or {@code manual}. + * + * @throws SecurityException If a security manager exists and its + * {@code checkPropertyAccess} method does not + * allow access to the specified system property. + * This exception will never be thrown, but users + * will be warned by a warning message. + * @throws IllegalArgumentException If the auto-raise configuration either from + * the system property or environment variable + * is set to an unrecognized value. * - * @see #getMessage() + * @since 1.5.0 + * @see #getRaiseConfigFromSystemProps() */ - private String message = null; + static synchronized final String getRaiseConfig() { + String value = getRaiseConfigFromSystemProps(); + + if (value == null || (value != null && value.length() == 0)) { + try { + // If the auto-raise configuration from system property is not set, + // then it tries to retrieve from environment variables safely + value = System.getenv(raiseConfEnvName); + if (value == null || (value != null && value.length() == 0)) + return "auto"; + } catch (final SecurityException se) { + System.err.println(String.format( + "jmatrix: [Warning] SecurityException has been occurred internally " + + "while attempting to get \"%s\" from environment variables", + raiseConfEnvName + )); + } + } + + // Lower case the value if not null (undeclared) + value = value.toLowerCase(); + + if ( // Check for 'auto' value + (value.equals("auto") || value.equals("a")) || + (value.equals("yes") || value.equals("y")) + ) { + value = "auto"; + } else if ( // Check for 'manual' value + (value.equals("manual") || value.equals("m")) || + (value.equals("no") || value.equals("n")) + ) { + value = "manual"; + } else { + // Throw an exception if the value is not known + throw new IllegalArgumentException(String.format( + "Unknown value of '%s': %s", + raiseConfName, value + )); + } + return value; + } /** - * Indicates whether this exception is caused by another exception. + * Retrieves the value of the system property named {@value #raiseConfName}. + * + *

This method attempts to obtain the value of the system property named + * {@value #raiseConfName}. It handles any {@link SecurityException} that may occur + * during this process and logs a warning message if such an exception is + * encountered without throwing the exception. + * + * @implNote The method is synchronized to ensure thread safety. + * + * @return The value of the system property {@value #raiseConfName}, or {@code null} + * if the property is not found or a {@link SecurityException} occurs. + * + * @throws SecurityException If a security manager exists and its + * {@code checkPropertyAccess} method does not + * allow access to the specified system property. + * This exception will never be thrown, but users + * will be warned by a warning message. + * + * @since 1.5.0 + * @see #getRaiseConfig() */ - private boolean isCausedException = false; + static synchronized final String getRaiseConfigFromSystemProps() { + try { + return System.getProperty(raiseConfName); + } catch (final SecurityException se) { + // Print a warning message when SecurityException occurred + System.err.println(String.format( + "jmatrix: [Warning] SecurityException has been occurred internally " + + "while attempting to get \"%s\" from system properties", + raiseConfName + )); + } + return null; + } /** * Constructs a new {@code JMatrixBaseException} with no detail message. @@ -88,117 +301,289 @@ public class JMatrixBaseException extends RuntimeException */ public JMatrixBaseException() { super(); - this.stackTraceElements = this.getStackTrace(); + this.errcode = JMErrorCode.UNKERR; } /** * Constructs a new {@code JMatrixBaseException} with the specified detail message. * - * @param s the detail message. + * @param s The detail message and will be saved for later retrieval + * by the {@link #getMessage()} method. * * @since 1.0.0b.1 */ public JMatrixBaseException(String s) { super(s); this.message = s; - this.stackTraceElements = this.getStackTrace(); + this.errcode = JMErrorCode.UNKERR; // Set to 'Unknown error' } /** - * Constructs a new {@code JMatrixBaseException} with the specified cause - * and a detail message of the cause. + * Constructs a new {@code JMatrixBaseException} with the specified errno + * and the detail message. + * + * @param errno The error number. + * @param s The detail message and will be saved for later retrieval + * by the {@link #getMessage()} method. + * + * @since 1.5.0 + * @see #getMessage() + * @see #getErrorCode() + */ + public JMatrixBaseException(int errno, String s) { + super(s); + this.message = s; + this.errcode = JMErrorCode.valueOf(errno); + // Prevent from internal error due to null value + if (this.errcode == null) this.errcode = JMErrorCode.UNKERR; + } + + /** + * Constructs a new {@code JMatrixBaseException} with the specified cause. + * + *

This constructor allows for exception chaining, where the new exception + * is caused by an existing throwable. This is useful for wrapping lower-level + * exceptions in higher-level exceptions, providing more context about the error + * that occurred. + * + *

The detailed message for the new exception is derived from the cause: + *

+ * + *

Additionally, this constructor attempts to propagate the error code from + * the cause to the new exception: + *

* - * @param cause the cause of this exception. + * @param cause The cause of this exception. A {@code null} value is permitted + * and indicates that the cause is nonexistent or unknown. * - * @since 1.0.0b.1 + * @since 1.0.0b.1 */ public JMatrixBaseException(Throwable cause) { super(cause); - this.stackTraceElements = this.getStackTrace(); - this.causedStackTraceElements = cause.getStackTrace(); - this.strCause = cause.toString(); - this.message = cause.getMessage(); - this.isCausedException = true; + this.message = (cause == null) ? null : cause.toString(); + + // Get the error code if the `cause` is an instance of this class, + // if not, fallback to UNKERR error code + this.errcode = (cause instanceof JMatrixBaseException) + ? ((JMatrixBaseException) cause).errcode + : JMErrorCode.UNKERR; } /** - * Prints the stack trace of this exception to the standard error stream. + * Constructs a new {@code JMatrixBaseException} with the specified cause + * and a detailed message for later retrieval by {@link #getMessage()}. + * + *

This constructor allows for exception chaining, where the new exception + * is caused by an existing throwable. This is useful for wrapping lower-level + * exceptions in higher-level exceptions, providing more context about the error + * that occurred. + * + *

Additionally, this constructor attempts to propagate the error code from + * the cause to the new exception: + *

+ * + * @param s The descriptive message. + * @param cause The cause of this exception. A {@code null} value is permitted + * and indicates that the cause is nonexistent or unknown. + * + * @since 1.5.0 + */ + public JMatrixBaseException(String s, Throwable cause) { + super(s, cause); + this.message = s; + + // Get the error code if the `cause` is an instance of this class, + // if not, fallback to UNKERR error code + this.errcode = (cause instanceof JMatrixBaseException) + ? ((JMatrixBaseException) cause).errcode + : JMErrorCode.UNKERR; + } + + /** + * Constructs a new {@code JMatrixBaseException} with the specified detail message, + * cause, suppression enabled or disabled, and writable stack trace enabled or disabled. + * + *

The detail message is saved for later retrieval by the {@link #getMessage()} + * method. If {@code cause} is not {@code null}, the error code will be determined + * based on whether the cause is an instance of {@code JMatrixBaseException} of its + * subclasses. If it is, the error code will be inherited from the cause; otherwise, + * the error code will be set to {@link JMErrorCode#UNKERR}. + * + * @param message The detail message (which is saved for later retrieval + * by the {@link #getMessage()} method) + * @param cause The cause (which is saved for later retrieval by the + * {@link #getCause()} method). + * A {@code null} value is permitted, and indicates that + * the cause is nonexistent or unknown. + * @param enableSuppression Whether or not suppression is enabled or disabled. + * @param writableStackTrace Whether or not the stack trace should be writable. + * + * @since 1.5.0 + */ + protected JMatrixBaseException(String message, Throwable cause, + boolean enableSuppression, + boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + this.message = message; + + // Get the error code if the `cause` is an instance of this class, + // if not, fallback to UNKERR error code + this.errcode = (cause instanceof JMatrixBaseException) + ? ((JMatrixBaseException) cause).errcode + : JMErrorCode.UNKERR; + } + + /** + * Prints this throwable and its backtrace to the standard error stream ({@code System.err}). + * + *

The format of this information depends on the implementation, but the following + * example may be regarded as typical: + * + *

 
+     * /!\ EXCEPTION
+     * >>>>>>>>>>>>>
+     * com.mitsuki.jmatrix.exception.InvalidIndexException [INVIDX]: Given row index is out of bounds
+     *         at "Example.main" -> "Example.java": line 7
+     *         ...
+     *
+     * [EXCEPTION INFO]
+     * Type: com.mitsuki.jmatrix.exception.InvalidIndexException
+     * Code: INVIDX
+     * Message: Given row index is out of bounds
+     * 
+ * + *

Output above was produced by this code: + *

 
+     *   public class Example {
+     *      public static void main(String[] args) {
+     *          Matrix m = new Matrix(new double[][] {
+     *              { 10, 10, 10 },
+     *              { 20, 20, 20 }
+     *          });
+     *          m = m.dropRow(3);  // Throws
+     *      }
+     *   }
+     * 
* * @since 1.0.0b.1 */ @Override public void printStackTrace() { - String spc = " "; - String arrow = ">>>>>>>>>>>>>"; - - if (this.isCausedException && this.stackTraceElements.length > 2) { - System.err.printf("/!\\ EXCEPTION%s%s%s", - System.lineSeparator(), - arrow, - System.lineSeparator()); - System.err.println(this.toString()); - - for (int i = this.stackTraceElements.length - 2; i != this.stackTraceElements.length; i++) { - System.err.printf("%sat \"%s.%s\" -> \"%s\": line %d" + System.lineSeparator(), - spc, - this.stackTraceElements[i].getClassName(), - this.stackTraceElements[i].getMethodName(), - this.stackTraceElements[i].getFileName(), - this.stackTraceElements[i].getLineNumber()); - } - } else { - System.err.printf("/!\\ EXCEPTION%s%s%s", - System.lineSeparator(), - arrow, - System.lineSeparator()); - System.err.println(this.toString()); - - for (StackTraceElement ste : this.stackTraceElements) { - System.err.printf("%sat \"%s.%s\" -> \"%s\": line %d" + System.lineSeparator(), - spc, - ste.getClassName(), - ste.getMethodName(), - ste.getFileName(), - ste.getLineNumber()); + printStackTrace(System.err); + } + + /** + * {@inheritDoc} + * + * @since 1.5.0 + * @see #printStackTrace() + */ + @Override + public void printStackTrace(PrintStream stream) { + final String arrows = ">>>>>>>>>>>>>"; + final StackTraceElement[] trace = getStackTrace(); + final Throwable cause = getCause(); + + // First, print the title including the exception description + stream.printf("\n%s\n%s\n%s\n", MSG_CAPTION, arrows, this.toString()); + + if (trace.length != 0 /* detect UNASSIGNED_STACK */) { + for (StackTraceElement ste : trace) { + stream.printf(STACK_TRACE_FORMAT, + ste.getClassName(), //* class name + ste.getMethodName(), //* method name + ste.getFileName(), //* file name + ste.getLineNumber() //* line number + ); } } - if (this.isCausedException) { - System.err.printf("%s/!\\ CAUSED BY%s%s%s", - System.lineSeparator(), - System.lineSeparator(), - arrow, - System.lineSeparator()); - System.err.println(this.strCause); - - for (StackTraceElement cste : this.causedStackTraceElements) { - System.err.printf("%sat \"%s.%s\" -> \"%s\": line %d" + System.lineSeparator(), - spc, - cste.getClassName(), - cste.getMethodName(), - cste.getFileName(), - cste.getLineNumber()); + if (cause != null) { + // Retrieve the stack trace of cause exception + final StackTraceElement[] causeTrace = cause.getStackTrace(); + stream.printf("\n%s\n%s\n%s\n", CAUSE_CAPTION, arrows, getCause()); + + for (StackTraceElement cste : causeTrace) { + stream.printf(STACK_TRACE_FORMAT, + cste.getClassName(), //* class name + cste.getMethodName(), //* method name + cste.getFileName(), //* file name + cste.getLineNumber() //* line number + ); } } - System.err.println(System.lineSeparator() + "[EXCEPTION INFO]"); - System.err.println("Type: " + (this.isCausedException ? this.strCause.split(":\\s")[0] : this.getClass().getName())); - System.err.println("Message: " + this.message); + + stream.printf(EXCEPTION_INFO_FORMAT, + (cause == null) + ? this.getClass().getName() + : cause.toString().split(":\\s")[0], + this.getErrorCode().getCode(), + this.getMessage() + ); } /** - * Returns the detail message of this exception. - * - * @return the detail message. + * {@inheritDoc} * * @since 1.0.0b.1 */ @Override public String getMessage() { - return (this.message != null ? this.message : this.strCause); + Throwable cause = getCause(); + return (this.message != null) + ? this.message + : (cause != null) ? cause.toString() : this.errcode.getMessage(); } /** - * Returns a string representation of this exception, including the class name and the detail message. + * Returns the error code of this throwable. + * + * If the error code is {@code null} or not known, it returns {@link JMErrorCode#UNKERR UNKERR} error code. + * + * @return The {@link JMErrorCode} object which contains the message and the error number. + * + * @since 1.5.0 + * @see JMErrorCode + */ + public synchronized JMErrorCode getErrorCode() { + return (this.errcode != null) ? this.errcode : JMErrorCode.UNKERR; + } + + /** + * {@inheritDoc} + * + * @since 1.5.0 + */ + @Override + public synchronized Throwable getCause() { + // Return null if the cause is a reference to this class + Throwable cause = super.getCause(); + return (cause == this) ? null : cause; + } + + /** + * Returns a string representation of this exception, including the class name, error code, and + * the detail message. * * @return a string representation of this exception. * @@ -206,6 +591,140 @@ public String getMessage() { */ @Override public String toString() { - return String.format("%s: %s", this.getClass().getName(), (this.isCausedException ? this.strCause : this.message)); + JMErrorCode ec = getErrorCode(); + return String.format(ERR_MSG_WITH_CODE_FORMAT, + getClass().getName(), + ec.getCode(), + (this.message != null) ? this.message : ec.getMessage() + ); + } + + + /** + * Checks whether the current value of auto-raise configuration is set to {@code auto}. + * + * @return {@code true} if the auto-raise configuration is set to {@code auto}. + * Otherwise, returns {@code false}. + * + * @implNote The method is synchronized to ensure thread safety. + * + * @since 1.5.0 + */ + public static synchronized boolean isAutoRaise() { + raiseConf = getRaiseConfig(); + return raiseConf.equals("auto"); + } + + /** + * Raises an exception based on the specified cause, with an automatically + * determined error number. + * + *

This method provides a mechanism to either print the stack trace of a + * given exception and exit the application with a specified error code, or + * throw the exception, depending on the configuration set by the + * {@value #raiseConfName} environment variable. The error number is determined + * based on the type of the provided exception. + * + *

If the {@link #isAutoRaise()} method returns {@code true}, indicating + * that the {@value #raiseConfName} environment variable is set to {@code auto}: + *

+ * + *

If the {@link #isAutoRaise()} returns {@code false}, the provided exception + * {@code cause} is thrown, allowing it to propagate up the call stack. This + * behavior supports scenarios where the application prefers to handle exceptions + * using traditional try-catch blocks rather than exiting. + * + *

The error number is determined as follows: + *

+ * + * @param The type of the runtime exception to be raised. + * @param cause The exception that caused the error. This exception will either + * be printed and cause the application to exit, or will be thrown + * based on the configuration. + * + * @throws E If the auto-raise configuration is not set to {@code auto}, + * the method throws the provided exception {@code cause}. + * + * @since 1.5.0 + * @see #raise(RuntimeException, int) + */ + public static void raise(E cause) { + raise(cause, + // Retrieve the errno if the given object is an instance of this class + // Otherwise, set the errno to 400 (Unknown error) + (cause instanceof JMatrixBaseException) + ? ((JMatrixBaseException) cause).errcode.getErrno() + : JMErrorCode.UNKERR.getErrno() + ); + } + + /** + * Raises an exception based on the specified cause and error number. + * + *

This method provides a mechanism to either print the stack trace of a + * given exception and exit the application with a specified error code, or + * throw the exception, depending on the configuration set by the + * {@value #raiseConfName} environment variable. This allows for configurable + * error handling that can be controlled through environment settings. + * + *

It will checks whether the auto-raise configuration is set to {@code auto} + * utilizing the {@link #isAutoRaise()} method, if the {@link #isAutoRaise()} + * method returns {@code true}, indicating that the auto-raise configuration + * is set to {@code auto}: + *

    + *
  • The stack trace of the provided exception {@code cause} is printed to + * the standard error stream. + *
  • The application then exits with the specified error number {@code errno}. + * If {@code errno} is less than {@link Integer#MIN_VALUE} ({@value + * Integer#MIN_VALUE}), it is set to {@link Integer#MIN_VALUE} to ensure + * it is within valid range. + *
  • An error message indicating the exit code is printed to the standard + * error stream. + *
+ * + *

If {@link #isAutoRaise()} returns {@code false}, the provided exception + * {@code cause} is thrown, allowing it to propagate up the call stack. This + * behavior supports scenarios where the application prefers to handle exceptions + * using traditional try-catch blocks rather than exiting. + * + * @param The type of the runtime exception to be raised. + * @param cause The exception that caused the error. This exception will either + * be printed and cause the application to exit, or will be thrown + * based on the configuration. + * @param errno The error number to exit with if auto-raise is enabled. This + * number should be a valid integer within the range + * {@link Integer#MIN_VALUE} to {@link Integer#MAX_VALUE}. If the + * provided value is less than {@link Integer#MIN_VALUE}, it will + * be adjusted to {@link Integer#MIN_VALUE}. + * + * @throws E If the auto-raise configuration is not set to {@code auto}, + * the method throws the provided exception {@code cause}. + * + * @since 1.5.0 + * @see #raise(RuntimeException) + */ + public static void raise(E cause, int errno) { + if (isAutoRaise()) { + // Print the stack trace and exit with the specified errno + cause.printStackTrace(); + errno = (errno < Integer.MIN_VALUE) ? Integer.MIN_VALUE : errno; + System.err.printf("\nExited with error code: %d\n", errno); + System.exit(errno); + } else { + throw cause; // Throw the cause if not auto raise + } } } diff --git a/src/main/java/com/mitsuki/jmatrix/exception/NullMatrixException.java b/src/main/java/com/mitsuki/jmatrix/exception/NullMatrixException.java index 58e9b7f..214f547 100644 --- a/src/main/java/com/mitsuki/jmatrix/exception/NullMatrixException.java +++ b/src/main/java/com/mitsuki/jmatrix/exception/NullMatrixException.java @@ -19,6 +19,10 @@ package com.mitsuki.jmatrix.exception; +import com.mitsuki.jmatrix.Matrix; +import com.mitsuki.jmatrix.core.MatrixUtils; +import com.mitsuki.jmatrix.enums.JMErrorCode; + /** * Thrown when an operation is attempted on uninitialized matrices. This exception * indicates that the matrices involved in the operation have not been initialized @@ -32,7 +36,7 @@ *

Type: Unchecked exception

* *

The uninitialized or {@code null} matrices can be identified by - * using {@link com.mitsuki.jmatrix.core.MatrixUtils#isNullEntries(Matrix)}. + * using {@link MatrixUtils#isNullEntries(Matrix)}. * Or can be manually, for example: * *

 
@@ -44,7 +48,7 @@
  * 
* * @since 0.1.0 - * @version 1.3, 18 July 2023 + * @version 1.4, 10 June 2024 * @author * Ryuu Mitsuki * @license @@ -52,8 +56,7 @@ * * @see com.mitsuki.jmatrix.exception.JMatrixBaseException */ -public class NullMatrixException extends JMatrixBaseException -{ +public class NullMatrixException extends JMatrixBaseException { /** * Stores the serial version number of this class for deserialization to @@ -62,14 +65,10 @@ public class NullMatrixException extends JMatrixBaseException * * @see java.io.Serializable */ - private static final long serialVersionUID = 6_418_048_608L; + private static final long serialVersionUID = 43_003_192_023_203L; - /** - * Stores a string represents the detail message of this exception. - * - * @see #getMessage() - */ - private String message = null; + /** Stores the default errno of this exception based on {@link JMErrorCode}. */ + private static int defaultErrno = JMErrorCode.NULLMT.getErrno(); /** * Constructs a new {@code NullMatrixException} with no detail message. @@ -78,7 +77,7 @@ public class NullMatrixException extends JMatrixBaseException * @since 0.1.0 */ public NullMatrixException() { - super(); + super(defaultErrno, null); } /** @@ -89,46 +88,119 @@ public NullMatrixException() { * @since 0.1.0 */ public NullMatrixException(String message) { - super(message); - this.message = message; + super(defaultErrno, message); } /** - * Constructs a new {@code NullMatrixException} with the specified cause - * and a detail message of the cause. + * Constructs a new {@code NullMatrixException} with the specified errno + * and the detail message. + * + *

The given errno will replace the default errno of this exception. + * + * @param errno The error number. + * @param message The detail message and will be saved for later retrieval + * by the {@link #getMessage()} method. + * + * @since 1.5.0 + * @see #getMessage() + * @see #getErrorCode() + */ + public NullMatrixException(int errno, String message) { + super(errno, message); + defaultErrno = errno; + } + + /** + * Constructs a new {@code NullMatrixException} with the specified cause. * - * @param cause the cause of this exception. + *

This constructor allows for exception chaining, where the new exception + * is caused by an existing throwable. This is useful for wrapping lower-level + * exceptions in higher-level exceptions, providing more context about the error + * that occurred. * - * @since 0.1.0 + * @param cause The cause of this exception. A {@code null} value is permitted + * and indicates that the cause is nonexistent or unknown. + * + * @since 0.1.0 */ public NullMatrixException(Throwable cause) { super(cause); - this.message = cause.getMessage(); } + /** + * Constructs a new {@code NullMatrixException} with the specified cause + * and a detailed message for later retrieval by {@link #getMessage()}. + * + *

This constructor allows for exception chaining, where the new exception + * is caused by an existing throwable. This is useful for wrapping lower-level + * exceptions in higher-level exceptions, providing more context about the error + * that occurred. + * + * @param s The descriptive message. + * @param cause The cause of this exception. A {@code null} value is permitted + * and indicates that the cause is nonexistent or unknown. + * + * @since 1.5.0 + */ + public NullMatrixException(String s, Throwable cause) { + super(s, cause); + } /** - * Returns the detail message of this exception. + * Constructs a new {@code NullMatrixException} with the specified detail message, + * cause, suppression enabled or disabled, and writable stack trace enabled or disabled. + * + * @param s The detail message (which is saved for later retrieval + * by the {@link #getMessage()} method) + * @param cause The cause (which is saved for later retrieval by the + * {@link #getCause()} method). + * A {@code null} value is permitted, and indicates that + * the cause is nonexistent or unknown. + * @param enableSuppression Whether or not suppression is enabled or disabled. + * @param writableStackTrace Whether or not the stack trace should be writable. * - * @return the detail message. + * @since 1.5.0 + */ + protected NullMatrixException(String s, Throwable cause, + boolean enableSuppression, + boolean writableStackTrace) { + super(s, cause, enableSuppression, writableStackTrace); + } + + + /** + * {@inheritDoc} * * @since 0.1.0 */ @Override public String getMessage() { - return this.message; + return super.getMessage(); } /** - * Returns a string representation of this exception, including - * the class name and the detail message. + * {@inheritDoc} * - * @return a string representation of this exception. + * @since 1.5.0 + */ + @Override + public JMErrorCode getErrorCode() { + return JMErrorCode.valueOf(defaultErrno); + } + + /** + * {@inheritDoc} * * @since 0.1.0 */ @Override public String toString() { - return String.format("%s: %s", this.getClass().getName(), this.message); + JMErrorCode ec = this.getErrorCode(); + String errmsg = this.getMessage(); + return String.format(this.ERR_MSG_WITH_CODE_FORMAT, + this.getClass().getName(), + ec.getCode(), + (errmsg != null) ? errmsg : ec.getMessage() + ); } } diff --git a/src/test/java/com/mitsuki/jmatrix/test/enums/Test_JMErrorCode.java b/src/test/java/com/mitsuki/jmatrix/test/enums/Test_JMErrorCode.java new file mode 100644 index 0000000..d5371c9 --- /dev/null +++ b/src/test/java/com/mitsuki/jmatrix/test/enums/Test_JMErrorCode.java @@ -0,0 +1,177 @@ +package com.mitsuki.jmatrix.test.enums; + +import com.mitsuki.jmatrix.enums.JMErrorCode; + +import java.util.List; +import java.util.ArrayList; +import java.util.Arrays; +import org.junit.Before; +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * Test suite for the {@link JMErrorCode} enum. + * + *

This class contains various test methods to verify the functionality + * of the {@link JMErrorCode} enum, including tests for retrieving error numbers, + * error codes, error messages, and custom string representations. + * + * @since 1.5.0 + * @version 1.0, 17 June 2024 + * @author + * Ryuu Mitsuki + * @license + * Apache License 2.0 + * @see JMErrorCode + */ +public class Test_JMErrorCode { + /** + * A list of expected error numbers for each {@link JMErrorCode} value. + */ + List expectedErrno; + /** + * A list of expected string representations of error numbers for each + * {@link JMErrorCode} value. + */ + List expectedErrnoStr; + /** + * A list of expected error codes for each {@link JMErrorCode} value. + */ + List expectedErrcode; + /** + * The format string for the custom {@link JMErrorCode#toString()} method. + */ + String toStringFormat = "%s[%s]"; + + /** + * Initializes the expected error numbers, error codes, and their string + * representations before each test is run. + */ + @Before + public void setup() { + expectedErrno = Arrays.asList( + 201, // INVIDX + 202, // INVTYP + 203, // NULLMT + 400 // UNKERR + ); + + expectedErrcode = Arrays.asList( + "INVIDX", // 201 + "INVTYP", // 202 + "NULLMT", // 203 + "UNKERR" // 400 + ); + + expectedErrnoStr = new ArrayList<>(expectedErrno.size()); + for (Integer errno : expectedErrno) { + expectedErrnoStr.add("JM" + errno); + } + } + + /** + * Tests the {@link JMErrorCode#getErrno()} method to ensure that it returns + * the correct error number for each {@link JMErrorCode} value. + */ + @Test + public void test_getErrno() { + int n = 0; + for (JMErrorCode ec : JMErrorCode.values()) { + int actualErrno = ec.getErrno(); + assertTrue(expectedErrno.get(n++) == actualErrno); + } + } + + /** + * Tests the {@link JMErrorCode#getErrnoStr()} method to ensure that it returns + * the correct string representation of the error number for each {@link + * JMErrorCode} value. + */ + @Test + public void test_getErrnoStr() { + int n = 0; + for (JMErrorCode ec : JMErrorCode.values()) { + String actualErrnoStr = ec.getErrnoStr(); + assertNotNull(actualErrnoStr); + assertEquals(expectedErrnoStr.get(n++), actualErrnoStr); + } + } + + /** + * Tests the {@link JMErrorCode#getCode()} method to ensure that it returns + * the correct error code for each {@link JMErrorCode} value. + */ + @Test + public void test_getErrcode() { + int n = 0; + for (JMErrorCode ec : JMErrorCode.values()) { + String actualCode = ec.getCode(); + assertNotNull(actualCode); + assertEquals(expectedErrcode.get(n++), actualCode); + } + } + + /** + * Tests the {@link JMErrorCode#getMessage()} method to ensure that it returns + * a non-null message for each {@link JMErrorCode} value. + */ + @Test + public void test_getMessage() { + for (JMErrorCode ec : JMErrorCode.values()) { + String actualMessage = ec.getMessage(); + // Currently, only test for non-null + assertNotNull(actualMessage); + } + } + + /** + * Tests the {@link JMErrorCode#toString()} method to ensure that it returns + * the correct custom string representation for each {@link JMErrorCode} value. + */ + @Test + public void test_toString() { + int n = 0; + for (JMErrorCode ec : JMErrorCode.values()) { + String expectedString = String.format( + toStringFormat, expectedErrcode.get(n), expectedErrno.get(n)); + String actualString = ec.toString(); + assertNotNull(actualString); + assertEquals(expectedString, actualString); + n++; + } + } + + /** + * Tests the {@link JMErrorCode#valueOf(Object)} method to ensure that + * they return the correct {@link JMErrorCode} value based on the input. + */ + @Test + public void test_valueOf() { + // Test using a string (error code) + JMErrorCode fromStr = JMErrorCode.valueOf("NULLMT"); + assertNotNull(fromStr); + assertEquals("NULLMT", fromStr.getCode()); + assertTrue(203 == fromStr.getErrno()); + + // Test using an integer (errno) + JMErrorCode fromInt = JMErrorCode.valueOf(201); + assertNotNull(fromInt); + assertEquals("INVIDX", fromInt.getCode()); + assertTrue(201 == fromInt.getErrno()); + + // Test using unknown type + JMErrorCode nullEC = JMErrorCode.valueOf(new int[1]); + assertNull(nullEC); + assertNotSame(fromStr, nullEC); + assertNotSame(fromInt, nullEC); + } + + /** + * Tests the {@link JMErrorCode#valueOf(String)} method to ensure that it throws + * an {@link IllegalArgumentException} when an unknown error code is provided. + */ + @Test(expected=java.lang.IllegalArgumentException.class) + public void test_valueOfThrows() { + JMErrorCode.valueOf("UNKNOWN"); + } +}