NUMBERS-68: Implement "parse" as the inverse function of "toString".
authorGilles Sadowski <gilles@harfang.homelinux.org>
Wed, 25 Apr 2018 09:41:04 +0000 (11:41 +0200)
committerGilles Sadowski <gilles@harfang.homelinux.org>
Wed, 25 Apr 2018 09:41:04 +0000 (11:41 +0200)
The string representation handled by both methods is fixed (as is the case
for the JDK number classes).

commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/Complex.java
commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/ComplexTest.java

index 217c109..2cd2e0f 100644 (file)
@@ -20,8 +20,8 @@ package org.apache.commons.numbers.complex;
 import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.List;
-import java.lang.NumberFormatException;
 import org.apache.commons.numbers.core.Precision;
+
 /**
  * Representation of a Complex number, i.e. a number which has both a
  * real and imaginary part.
@@ -133,37 +133,33 @@ public final class Complex implements Serializable  {
     }
 
     /**
-     * Parses a text expression in a String object to produce
-     * a Cartesian complex number. Acceptable formats are:
-     * <p><ul> * <li>Single number (parsed as real). Example: "3.14"
-     * <li>Single number with appended "i" (parsed as imaginary): "1.42i"
-     * <li>Pair of numbers, separated by plus sign and with appended i: "3 + 4i"
-     * </ul>
-     * @throws{ParseException, NumberFormatException}
+     * Parses a string that would be produced by {@link #toString()}
+     * and instantiates the corresponding object.
+     *
+     * @param s String representation.
+     * @return an instance.
+     * @throws IllegalArgumentException if the string does not
+     * conform to the specification.
      */
     public static Complex parse(String s) {
-        final String[] terms = s.split("+");
-        if (terms.length == 2) {
-            if (terms[1].indexOf("i") == terms[1].length()) {
-                String imagTerm = (String) terms[1].subSequence(0, terms[1].length()-1);
-                final double real = Double.parseDouble(terms[0]);
-                final double imaginary = Double.parseDouble(imagTerm);
-                return Complex.ofCartesian(real, imaginary);
-            } else {
-                throw new NumberFormatException("Expression must end with unique \"i\"");
-            }
-        } else if (terms.length == 1){
-            if (terms[0].indexOf("i") == terms[0].length()) {
-                String imagTerm = (String) terms[0].subSequence(0, terms[0].length()-1);
-                final double imaginary = Double.parseDouble(imagTerm);
-                return Complex.ofCartesian(0, imaginary);
-            } else {
-                final double real = Double.parseDouble(terms[0]);
-                return Complex.ofReal(real);
-            }
-        } else {
-            throw new NumberFormatException("Invalid expression for Complex. See documentation for further information.");
+        final int len = s.length();
+        final int startParen = s.indexOf("(");
+        if (startParen != 0) {
+            throw new ComplexParsingException("Missing start parenthesis");
+        }
+        final int endParen = s.indexOf(")");
+        if (endParen != len - 1) {
+            throw new ComplexParsingException("Missing end parenthesis");
         }
+        final int comma = s.indexOf(",");
+        if (comma == -1) {
+            throw new ComplexParsingException("Missing comma");
+        }
+
+        final double re = Double.parseDouble(s.substring(startParen + 1, comma));
+        final double im = Double.parseDouble(s.substring(comma + 1, endParen));
+
+        return ofCartesian(re, im);
     }
 
     /**
@@ -1362,4 +1358,13 @@ public final class Complex implements Serializable  {
             d != 0;
     }
 
+    /** See {@link #parse(String)}. */
+    private static class ComplexParsingException extends IllegalArgumentException {
+        /**
+         * @param msg Error message.
+         */
+        ComplexParsingException(String msg) {
+            super(msg);
+        }
+    }
 }
index 8247459..ad73c14 100644 (file)
@@ -836,4 +836,57 @@ public class ComplexTest {
         Assert.assertTrue(Double.isNaN(zeroNaN.getArgument()));
         Assert.assertTrue(Double.isNaN(NAN.getArgument()));
     }
+
+    @Test
+    public void testParse() {
+        Assert.assertTrue(Complex.ZERO.equals(Complex.parse(Complex.ZERO.toString())));
+        Assert.assertTrue(Complex.ONE.equals(Complex.parse(Complex.ONE.toString())));
+        Assert.assertTrue(Complex.I.equals(Complex.parse(Complex.I.toString())));
+        Assert.assertTrue(Complex.INF.equals(Complex.parse(Complex.INF.toString())));
+        Assert.assertTrue(NAN.equals(Complex.parse(NAN.toString())));
+        Assert.assertTrue(oneInf.equals(Complex.parse(oneInf.toString())));
+        Assert.assertTrue(negInfZero.equals(Complex.parse(negInfZero.toString())));
+        Assert.assertTrue(Complex.ofReal(pi).equals(Complex.parse(Complex.ofReal(pi).toString())));
+        Assert.assertTrue(Complex.ofPolar(2, pi).equals(Complex.parse(Complex.ofPolar(2, pi).toString())));
+        Assert.assertTrue(Complex.ofCis(pi).equals(Complex.parse(Complex.ofCis(pi).toString())));
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testParseWrongStart() {
+        final String re = "1.234";
+        final String im = "5.678";
+        Complex.parse(re + "," + im + ")");
+    }
+    @Test(expected=IllegalArgumentException.class)
+    public void testParseWrongEnd() {
+        final String re = "1.234";
+        final String im = "5.678";
+        Complex.parse("(" + re + "," + im);
+    }
+    @Test(expected=IllegalArgumentException.class)
+    public void testParseMissingSeparator() {
+        final String re = "1.234";
+        final String im = "5.678";
+        Complex.parse("(" + re + " " + im + ")");
+    }
+    @Test(expected=IllegalArgumentException.class)
+    public void testParseInvalidRe() {
+        final String re = "I.234";
+        final String im = "5.678";
+        Complex.parse("(" + re + "," + im + ")");
+    }
+    @Test(expected=IllegalArgumentException.class)
+    public void testParseInvalidIm() {
+        final String re = "1.234";
+        final String im = "5.G78";
+        Complex.parse("(" + re + "," + im + ")");
+    }
+
+    @Test
+    public void testParseSpaceAllowedAroundNumbers() {
+        final double re = 1.234;
+        final double im = 5.678;
+        final String str = "(  " + re + "  , " + im + "     )";
+        Assert.assertTrue(Complex.ofCartesian(re, im).equals(Complex.parse(str)));
+    }
 }