You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@daffodil.apache.org by ja...@apache.org on 2018/07/09 14:52:41 UTC

[incubator-daffodil] 04/13: Almost done with parser

This is an automated email from the ASF dual-hosted git repository.

jadams pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-daffodil.git

commit fa0b330b8d9d98fb893f33a5229610bba73d9ead
Author: Josh Adams <ja...@tresys.com>
AuthorDate: Wed Apr 18 10:13:34 2018 -0400

    Almost done with parser
---
 .../grammar/primitives/PrimitivesZoned.scala       | 159 ++++++-
 .../org/apache/daffodil/util/DecimalUtils.scala    |  28 +-
 .../apache/daffodil/util/TestDecimalUtils.scala    | 180 ++++----
 .../unparsers/ConvertTextNumberUnparser.scala      |  42 +-
 .../processors/parsers/ZonedTextParsers.scala      | 493 ++++++++++++++++++++-
 5 files changed, 757 insertions(+), 145 deletions(-)

diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesZoned.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesZoned.scala
index ecad042..3d795d0 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesZoned.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesZoned.scala
@@ -17,13 +17,158 @@
 
 package org.apache.daffodil.grammar.primitives
 
-import org.apache.daffodil.dsom.ElementBase
+import org.apache.daffodil.dsom._
+import org.apache.daffodil.grammar.Gram
+import org.apache.daffodil.grammar.Terminal
+import com.ibm.icu.text.DecimalFormat
+import org.apache.daffodil.processors.unparsers.Unparser
+import org.apache.daffodil.processors.unparsers.ConvertZonedNumberUnparser
+import org.apache.daffodil.processors.unparsers.ConvertZonedCombinatorUnparser
+import java.math.{ BigDecimal => JBigDecimal, BigInteger => JBigInt }
+import org.apache.daffodil.processors.parsers.ConvertZonedByteParserUnparserHelper
+import org.apache.daffodil.processors.parsers.ConvertZonedCombinatorParser
+import org.apache.daffodil.processors.parsers.ConvertZonedDecimalParserUnparserHelper
+import org.apache.daffodil.processors.parsers.ConvertZonedIntParserUnparserHelper
+import org.apache.daffodil.processors.parsers.ConvertZonedIntegerParserUnparserHelper
+import org.apache.daffodil.processors.parsers.ConvertZonedLongParserUnparserHelper
+import org.apache.daffodil.processors.parsers.ConvertZonedNonNegativeIntegerParserUnparserHelper
+import org.apache.daffodil.processors.parsers.ConvertZonedNumberParser
+import org.apache.daffodil.processors.parsers.ConvertZonedNumberParserUnparserHelperBase
+import org.apache.daffodil.processors.parsers.ConvertZonedShortParserUnparserHelper
+import org.apache.daffodil.processors.parsers.ConvertZonedUnsignedByteParserUnparserHelper
+import org.apache.daffodil.processors.parsers.ConvertZonedUnsignedLongParserUnparserHelper
+import org.apache.daffodil.processors.parsers.ConvertZonedUnsignedShortParserUnparserHelper
+import org.apache.daffodil.processors.parsers.ZonedFormatFactoryBase
+import org.apache.daffodil.processors.parsers.ZonedFormatFactoryDynamic
+import org.apache.daffodil.processors.parsers.ZonedFormatFactoryStatic
+import org.apache.daffodil.processors.parsers.ConvertZonedUnsignedIntParserUnparserHelper
+import org.apache.daffodil.processors.parsers.Parser
 
-abstract class ZonedTextNumberPrim(e: ElementBase, guard: Boolean) extends UnimplementedPrimitive(e, guard) {
-  // lazy val parser: DaffodilParser = new ZonedTextNumberParser(e.elementRuntimeData)
+case class ConvertZonedCombinator(e: ElementBase, value: Gram, converter: Gram)
+  extends Terminal(e, !(value.isEmpty || converter.isEmpty)) {
+
+  lazy val parser = new ConvertZonedCombinatorParser(e.termRuntimeData, value.parser, converter.parser)
+
+  override lazy val unparser = new ConvertZonedCombinatorUnparser(e.termRuntimeData, value.unparser, converter.unparser)
+}
+
+abstract class ConvertZonedNumberPrim[S](e: ElementBase)
+  extends Terminal(e, true) {
+
+  def helper: ConvertZonedNumberParserUnparserHelperBase[S]
+
+  def numFormatFactory: ZonedFormatFactoryBase[S] = {
+    val h = helper
+
+    val (pattern, patternStripped) = {
+      val p = e.textNumberPattern
+
+      val noEscapedTicksRegex = """''""".r
+      val patternNoEscapedTicks = noEscapedTicksRegex.replaceAllIn(p, "")
+      val noQuotedRegex = """'[^']+'""".r
+      val patternNoQuoted = noQuotedRegex.replaceAllIn(patternNoEscapedTicks, "")
+
+      if (patternNoQuoted.contains("V")) {
+        e.notYetImplemented("textNumberPattern with V symbol")
+      }
+
+      if (patternNoQuoted.contains("P")) {
+        e.notYetImplemented("textNumberPattern with P symbol")
+      }
+
+      if (patternNoQuoted.contains("@")) {
+        e.SDE("The '@' symbol may not be used in textNumberPattern for textNumberRep='zoned'")
+      }
+
+      if (patternNoQuoted.contains(";")) {
+        e.SDE("Negative patterns may not be used in textNumberPattern for textNumberRep='zoned'")
+      }
+
+      e.primType match {
+        case PrimType.Double | PrimType.Float => e.SDE("textZonedFormat='zoned' does not support Doubles/Floats")
+        case PrimType.UnsignedLong | PrimType.UnsignedInt | PrimType.UnsignedShort | PrimType.UnsignedByte if (e.textNumberCheckPolicy == TextNumberCheckPolicy.Lax) => {
+          if ((patternNoQuoted.charAt(0) != '+') || (patternNoQuoted.charAt(patternNoQuoted.length) != '+'))
+            e.SDE("textNumberPattern must have '+' at the beginning or the end of the pattern when textZonedFormat='zoned' and textNumberPolicy='lax' for unsigned numbers")
+        }
+        case _ => {
+          if ((patternNoQuoted.charAt(0) != '+') || (patternNoQuoted.charAt(patternNoQuoted.length) != '+'))
+            e.SDE("textNumberPattern must have '+' at the beginning or the end of the pattern when textZonedFormat='zoned' for signed numbers")
+        }
+      }
+
+      // Load the pattern to make sure it is valid
+      try {
+        new DecimalFormat(p)
+      } catch {
+        case ex: IllegalArgumentException => e.SDE("Invalid textNumberPattern: " + ex.getMessage())
+      }
+
+      (p, patternNoQuoted)
+    }
+
+    val nff = if (isConstant) {
+      new ZonedFormatFactoryStatic[S](e.termRuntimeData, h,
+        e.textNumberCheckPolicy,
+        pattern,
+        e.textNumberRounding,
+        roundingMode,
+        roundingIncrement)
+    } else {
+      new ZonedFormatFactoryDynamic[S](e.termRuntimeData, h,
+        e.textNumberCheckPolicy,
+        pattern,
+        e.textNumberRounding,
+        roundingMode,
+        roundingIncrement)
+    }
+    nff
+  }
+
+  lazy val parser: Parser = new ConvertZonedNumberParser[S](helper, numFormatFactory, e.textZonedSignStyle, e.elementRuntimeData)
+
+  override lazy val unparser: Unparser = new ConvertZonedNumberUnparser[S](helper, e.textZonedSignStyle, e.elementRuntimeData)
+}
+
+case class ConvertZonedIntegerPrim(e: ElementBase) extends ConvertZonedNumberPrim[JBigInt](e) {
+  val helper = new ConvertZonedIntegerParserUnparserHelper[JBigInt]()
 }
 
-case class ZonedTextBytePrim(el: ElementBase) extends ZonedTextNumberPrim(el, false)
-case class ZonedTextShortPrim(el: ElementBase) extends ZonedTextNumberPrim(el, false)
-case class ZonedTextIntPrim(el: ElementBase) extends ZonedTextNumberPrim(el, false)
-case class ZonedTextLongPrim(el: ElementBase) extends ZonedTextNumberPrim(el, false)
+case class ConvertZonedDecimalPrim(e: ElementBase) extends ConvertZonedNumberPrim[JBigDecimal](e) {
+  val helper = new ConvertZonedDecimalParserUnparserHelper[JBigDecimal]()
+}
+
+case class ConvertZonedNonNegativeIntegerPrim(e: ElementBase) extends ConvertZonedNumberPrim[JBigInt](e) {
+  val helper = new ConvertZonedNonNegativeIntegerParserUnparserHelper[JBigDecimal]()
+}
+
+case class ConvertZonedLongPrim(e: ElementBase) extends ConvertZonedNumberPrim[Long](e) {
+  val helper = new ConvertZonedLongParserUnparserHelper[Long]()
+}
+
+case class ConvertZonedIntPrim(e: ElementBase) extends ConvertZonedNumberPrim[Int](e) {
+  val helper = new ConvertZonedIntParserUnparserHelper[Int]()
+}
+
+case class ConvertZonedShortPrim(e: ElementBase) extends ConvertZonedNumberPrim[Short](e) {
+  val helper = new ConvertZonedShortParserUnparserHelper[Short]()
+}
+
+case class ConvertZonedBytePrim(e: ElementBase) extends ConvertZonedNumberPrim[Byte](e) {
+  val helper = new ConvertZonedByteParserUnparserHelper[Byte]()
+}
+
+case class ConvertZonedUnsignedLongPrim(e: ElementBase) extends ConvertZonedNumberPrim[JBigInt](e) {
+  val helper = new ConvertZonedUnsignedLongParserUnparserHelper[JBigInt]()
+}
+
+case class ConvertZonedUnsignedIntPrim(e: ElementBase) extends ConvertZonedNumberPrim[Long](e) {
+  val helper = ConvertZonedUnsignedIntParserUnparserHelper[Long]()
+}
+
+case class ConvertZonedUnsignedShortPrim(e: ElementBase) extends ConvertZonedNumberPrim[Int](e) {
+  val helper = new ConvertZonedUnsignedShortParserUnparserHelper[Int]()
+}
+
+case class ConvertZonedUnsignedBytePrim(e: ElementBase) extends ConvertZonedNumberPrim[Short](e) {
+  val helper = new ConvertZonedUnsignedByteParserUnparserHelper[Short]()
+}
diff --git a/daffodil-lib/src/main/scala/org/apache/daffodil/util/DecimalUtils.scala b/daffodil-lib/src/main/scala/org/apache/daffodil/util/DecimalUtils.scala
index 5851369..9a733e8 100644
--- a/daffodil-lib/src/main/scala/org/apache/daffodil/util/DecimalUtils.scala
+++ b/daffodil-lib/src/main/scala/org/apache/daffodil/util/DecimalUtils.scala
@@ -412,7 +412,7 @@ object DecimalUtils {
       return (digit + 80).asInstanceOf[Char]
   }
 
-  def zonedToInt(num: String, zonedStyle: TextZonedSignStyle): Int = {
+  def zonedToNumber(num: String, zonedStyle: TextZonedSignStyle): String = {
     val decodedValue = new StringBuilder()
     var negative = false
 
@@ -424,29 +424,31 @@ object DecimalUtils {
         case TextZonedSignStyle.AsciiTandemModified => convertFromAsciiTandemModified(char)
       }
 
-      if ((digit < 0) && !negative)
+      if ((digit < 0) && !negative) {
         negative = true
+        decodedValue.append('-')
+      }
 
       decodedValue.append(java.lang.Math.abs(digit))
     }
 
-    return negative match {
-      case true => -Integer.parseInt(decodedValue.toString)
-      case false => Integer.parseInt(decodedValue.toString)
-    }
+    return decodedValue.toString
   }
 
-  def zonedFromInt(num: Int, zonedStyle: TextZonedSignStyle): String = {
-    val positive = (num >= 0)
-    val inChars = java.lang.Math.abs(num).toString.toCharArray
+  def zonedFromNumber(num: String, zonedStyle: TextZonedSignStyle): String = {
+    val negative = (num.charAt(0) == '-')
+    val inChars = negative match {
+      case true => num.substring(0).toCharArray
+      case false => num.toCharArray
+    }
     val encodedValue = new StringBuilder()
 
     for (char <- inChars) {
       val digit = zonedStyle match {
-        case TextZonedSignStyle.AsciiStandard => convertToAsciiStandard(char, positive)
-        case TextZonedSignStyle.AsciiTranslatedEBCDIC => convertToAsciiTranslatedEBCDIC(char, positive)
-        case TextZonedSignStyle.AsciiCARealiaModified => convertToAsciiCARealiaModified(char, positive)
-        case TextZonedSignStyle.AsciiTandemModified => convertToAsciiTandemModified(char, positive)
+        case TextZonedSignStyle.AsciiStandard => convertToAsciiStandard(char, negative)
+        case TextZonedSignStyle.AsciiTranslatedEBCDIC => convertToAsciiTranslatedEBCDIC(char, negative)
+        case TextZonedSignStyle.AsciiCARealiaModified => convertToAsciiCARealiaModified(char, negative)
+        case TextZonedSignStyle.AsciiTandemModified => convertToAsciiTandemModified(char, negative)
       }
 
       encodedValue.append(digit)
diff --git a/daffodil-lib/src/test/scala/org/apache/daffodil/util/TestDecimalUtils.scala b/daffodil-lib/src/test/scala/org/apache/daffodil/util/TestDecimalUtils.scala
index fc1c950..20674b1 100644
--- a/daffodil-lib/src/test/scala/org/apache/daffodil/util/TestDecimalUtils.scala
+++ b/daffodil-lib/src/test/scala/org/apache/daffodil/util/TestDecimalUtils.scala
@@ -1252,212 +1252,212 @@ class TestDecimalUtils {
 
   @Test def zonedIntAsciiStandardPos1() {
     val num = "1"
-    val result = zonedToInt(num, TextZonedSignStyle.AsciiStandard)
-    assertEquals(result, 1)
-    assertEquals(zonedFromInt(result, TextZonedSignStyle.AsciiStandard), num)
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiStandard)
+    assertEquals(result, "1")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiStandard), num)
   }
 
   @Test def zonedIntAsciiStandardPos2() {
     val num = "12"
-    val result = zonedToInt(num, TextZonedSignStyle.AsciiStandard)
-    assertEquals(result, 12)
-    assertEquals(zonedFromInt(result, TextZonedSignStyle.AsciiStandard), num)
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiStandard)
+    assertEquals(result, "12")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiStandard), num)
   }
 
   @Test def zonedIntAsciiStandardPos3() {
     val num = "123"
-    val result = zonedToInt(num, TextZonedSignStyle.AsciiStandard)
-    assertEquals(result, 123)
-    assertEquals(zonedFromInt(result, TextZonedSignStyle.AsciiStandard), num)
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiStandard)
+    assertEquals(result, "123")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiStandard), num)
   }
 
   @Test def zonedIntAsciiStandardPos4() {
     val num = "1234567890"
-    val result = zonedToInt(num, TextZonedSignStyle.AsciiStandard)
-    assertEquals(result, 1234567890)
-    assertEquals(zonedFromInt(result, TextZonedSignStyle.AsciiStandard), num)
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiStandard)
+    assertEquals(result, "1234567890")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiStandard), num)
   }
 
   @Test def zonedIntAsciiStandardPos5() {
     val num = "000000000001234567890"
-    val result = zonedToInt(num, TextZonedSignStyle.AsciiStandard)
-    assertEquals(result, 1234567890)
-    assertEquals(zonedFromInt(result, TextZonedSignStyle.AsciiStandard), "1234567890")
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiStandard)
+    assertEquals(result, "1234567890")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiStandard), "1234567890")
   }
 
   @Test def zonedIntAsciiStandardNeg1() {
     val num = "q"
-    val result = zonedToInt(num, TextZonedSignStyle.AsciiStandard)
-    assertEquals(result, -1)
-    assertEquals(zonedFromInt(result, TextZonedSignStyle.AsciiStandard), num)
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiStandard)
+    assertEquals(result, "-1")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiStandard), num)
   }
 
   @Test def zonedIntAsciiStandardNeg2() {
     val num = "qr"
-    val result = zonedToInt(num, TextZonedSignStyle.AsciiStandard)
-    assertEquals(result, -12)
-    assertEquals(zonedFromInt(result, TextZonedSignStyle.AsciiStandard), num)
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiStandard)
+    assertEquals(result, "-12")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiStandard), num)
   }
 
   @Test def zonedIntAsciiStandardNeg3() {
     val num = "qrs"
-    val result = zonedToInt(num, TextZonedSignStyle.AsciiStandard)
-    assertEquals(result, -123)
-    assertEquals(zonedFromInt(result, TextZonedSignStyle.AsciiStandard), num)
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiStandard)
+    assertEquals(result, "-123")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiStandard), num)
   }
 
   @Test def zonedIntAsciiStandardNeg4() {
     val num = "qrstuvwxyp"
-    val result = zonedToInt(num, TextZonedSignStyle.AsciiStandard)
-    assertEquals(result, -1234567890)
-    assertEquals(zonedFromInt(result, TextZonedSignStyle.AsciiStandard), num)
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiStandard)
+    assertEquals(result, "-1234567890")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiStandard), num)
   }
 
   @Test def zonedIntAsciiStandardNeg5() {
     val num = "pppppppppppqrstuvwxyp"
-    val result = zonedToInt(num, TextZonedSignStyle.AsciiStandard)
-    assertEquals(result, -1234567890)
-    assertEquals(zonedFromInt(result, TextZonedSignStyle.AsciiStandard), "qrstuvwxyp")
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiStandard)
+    assertEquals(result, "-1234567890")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiStandard), "qrstuvwxyp")
   }
 
   @Test def zonedIntAsciiCARealiaModifiedPos1() {
     val num = "1"
-    val result = zonedToInt(num, TextZonedSignStyle.AsciiCARealiaModified)
-    assertEquals(result, 1)
-    assertEquals(zonedFromInt(result, TextZonedSignStyle.AsciiCARealiaModified), num)
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiCARealiaModified)
+    assertEquals(result, "1")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiCARealiaModified), num)
   }
 
   @Test def zonedIntAsciiCARealiaModifiedPos2() {
     val num = "12"
-    val result = zonedToInt(num, TextZonedSignStyle.AsciiCARealiaModified)
-    assertEquals(result, 12)
-    assertEquals(zonedFromInt(result, TextZonedSignStyle.AsciiCARealiaModified), num)
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiCARealiaModified)
+    assertEquals(result, "12")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiCARealiaModified), num)
   }
 
   @Test def zonedIntAsciiCARealiaModifiedPos3() {
     val num = "123"
-    val result = zonedToInt(num, TextZonedSignStyle.AsciiCARealiaModified)
-    assertEquals(result, 123)
-    assertEquals(zonedFromInt(result, TextZonedSignStyle.AsciiCARealiaModified), num)
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiCARealiaModified)
+    assertEquals(result, "123")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiCARealiaModified), num)
   }
 
   @Test def zonedIntAsciiCARealiaModifiedPos4() {
     val num = "1234567890"
-    val result = zonedToInt(num, TextZonedSignStyle.AsciiCARealiaModified)
-    assertEquals(result, 1234567890)
-    assertEquals(zonedFromInt(result, TextZonedSignStyle.AsciiCARealiaModified), num)
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiCARealiaModified)
+    assertEquals(result, "1234567890")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiCARealiaModified), num)
   }
 
   @Test def zonedIntAsciiCARealiaModifiedPos5() {
     val num = "000000000001234567890"
-    val result = zonedToInt(num, TextZonedSignStyle.AsciiCARealiaModified)
-    assertEquals(result, 1234567890)
-    assertEquals(zonedFromInt(result, TextZonedSignStyle.AsciiCARealiaModified), "1234567890")
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiCARealiaModified)
+    assertEquals(result, "1234567890")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiCARealiaModified), "1234567890")
   }
 
   @Test def zonedIntAsciiCARealiaModifiedNeg1() {
     val num = "!"
-    val result = zonedToInt(num, TextZonedSignStyle.AsciiCARealiaModified)
-    assertEquals(result, -1)
-    assertEquals(zonedFromInt(result, TextZonedSignStyle.AsciiCARealiaModified), num)
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiCARealiaModified)
+    assertEquals(result, "-1")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiCARealiaModified), num)
   }
 
   @Test def zonedIntAsciiCARealiaModifiedNeg2() {
     val num = "!\""
-    val result = zonedToInt(num, TextZonedSignStyle.AsciiCARealiaModified)
-    assertEquals(result, -12)
-    assertEquals(zonedFromInt(result, TextZonedSignStyle.AsciiCARealiaModified), num)
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiCARealiaModified)
+    assertEquals(result, "-12")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiCARealiaModified), num)
   }
 
   @Test def zonedIntAsciiCARealiaModifiedNeg3() {
     val num = "!\"#"
-    val result = zonedToInt(num, TextZonedSignStyle.AsciiCARealiaModified)
-    assertEquals(result, -123)
-    assertEquals(zonedFromInt(result, TextZonedSignStyle.AsciiCARealiaModified), num)
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiCARealiaModified)
+    assertEquals(result, "-123")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiCARealiaModified), num)
   }
 
   @Test def zonedIntAsciiCARealiaModifiedNeg4() {
     val num = "!\"#$%&'() "
-    val result = zonedToInt(num, TextZonedSignStyle.AsciiCARealiaModified)
-    assertEquals(result, -1234567890)
-    assertEquals(zonedFromInt(result, TextZonedSignStyle.AsciiCARealiaModified), num)
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiCARealiaModified)
+    assertEquals(result, "-1234567890")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiCARealiaModified), num)
   }
 
   @Test def zonedIntAsciiCARealiaModifiedNeg5() {
     val num = "           !\"#$%&'() "
-    val result = zonedToInt(num, TextZonedSignStyle.AsciiCARealiaModified)
-    assertEquals(result, -1234567890)
-    assertEquals(zonedFromInt(result, TextZonedSignStyle.AsciiCARealiaModified), "!\"#$%&'() ")
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiCARealiaModified)
+    assertEquals(result, "-1234567890")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiCARealiaModified), "!\"#$%&'() ")
   }
 
   @Test def zonedIntAsciiTandemModifiedPos1() {
     val num = "1"
-    val result = zonedToInt(num, TextZonedSignStyle.AsciiTandemModified)
-    assertEquals(result, 1)
-    assertEquals(zonedFromInt(result, TextZonedSignStyle.AsciiTandemModified), num)
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiTandemModified)
+    assertEquals(result, "1")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiTandemModified), num)
   }
 
   @Test def zonedIntAsciiTandemModifiedPos2() {
     val num = "12"
-    val result = zonedToInt(num, TextZonedSignStyle.AsciiTandemModified)
-    assertEquals(result, 12)
-    assertEquals(zonedFromInt(result, TextZonedSignStyle.AsciiTandemModified), num)
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiTandemModified)
+    assertEquals(result, "12")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiTandemModified), num)
   }
 
   @Test def zonedIntAsciiTandemModifiedPos3() {
     val num = "123"
-    val result = zonedToInt(num, TextZonedSignStyle.AsciiTandemModified)
-    assertEquals(result, 123)
-    assertEquals(zonedFromInt(result, TextZonedSignStyle.AsciiTandemModified), num)
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiTandemModified)
+    assertEquals(result, "123")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiTandemModified), num)
   }
 
   @Test def zonedIntAsciiTandemModifiedPos4() {
     val num = "1234567890"
-    val result = zonedToInt(num, TextZonedSignStyle.AsciiTandemModified)
-    assertEquals(result, 1234567890)
-    assertEquals(zonedFromInt(result, TextZonedSignStyle.AsciiTandemModified), num)
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiTandemModified)
+    assertEquals(result, "1234567890")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiTandemModified), num)
   }
 
   @Test def zonedIntAsciiTandemModifiedPos5() {
     val num = "000000000001234567890"
-    val result = zonedToInt(num, TextZonedSignStyle.AsciiTandemModified)
-    assertEquals(result, 1234567890)
-    assertEquals(zonedFromInt(result, TextZonedSignStyle.AsciiTandemModified), "1234567890")
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiTandemModified)
+    assertEquals(result, "1234567890")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiTandemModified), "1234567890")
   }
 
   @Test def zonedIntAsciiTandemModifiedNeg1() {
     val num = ""
-    val result = zonedToInt(num, TextZonedSignStyle.AsciiTandemModified)
-    assertEquals(result, -1)
-    assertEquals(zonedFromInt(result, TextZonedSignStyle.AsciiTandemModified), num)
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiTandemModified)
+    assertEquals(result, "-1")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiTandemModified), num)
   }
 
   @Test def zonedIntAsciiTandemModifiedNeg2() {
     val num = "‚"
-    val result = zonedToInt(num, TextZonedSignStyle.AsciiTandemModified)
-    assertEquals(result, -12)
-    assertEquals(zonedFromInt(result, TextZonedSignStyle.AsciiTandemModified), num)
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiTandemModified)
+    assertEquals(result, "-12")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiTandemModified), num)
   }
 
   @Test def zonedIntAsciiTandemModifiedNeg3() {
     val num = "‚ƒ"
-    val result = zonedToInt(num, TextZonedSignStyle.AsciiTandemModified)
-    assertEquals(result, -123)
-    assertEquals(zonedFromInt(result, TextZonedSignStyle.AsciiTandemModified), num)
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiTandemModified)
+    assertEquals(result, "-123")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiTandemModified), num)
   }
 
   @Test def zonedIntAsciiTandemModifiedNeg4() {
     val num = "‚ƒ„…†‡ˆ‰€"
-    val result = zonedToInt(num, TextZonedSignStyle.AsciiTandemModified)
-    assertEquals(result, -1234567890)
-    assertEquals(zonedFromInt(result, TextZonedSignStyle.AsciiTandemModified), num)
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiTandemModified)
+    assertEquals(result, "-1234567890")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiTandemModified), num)
   }
 
   @Test def zonedIntAsciiTandemModifiedNeg5() {
     val num = "€€€€€€€€€€€‚ƒ„…†‡ˆ‰€"
-    val result = zonedToInt(num, TextZonedSignStyle.AsciiTandemModified)
-    assertEquals(result, -1234567890)
-    assertEquals(zonedFromInt(result, TextZonedSignStyle.AsciiTandemModified), "‚ƒ„…†‡ˆ‰€")
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiTandemModified)
+    assertEquals(result, "-1234567890")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiTandemModified), "‚ƒ„…†‡ˆ‰€")
   }
 
 }
diff --git a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/ConvertTextNumberUnparser.scala b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/ConvertTextNumberUnparser.scala
index 2efa11a..bfa31d4 100644
--- a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/ConvertTextNumberUnparser.scala
+++ b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/ConvertTextNumberUnparser.scala
@@ -17,15 +17,12 @@
 
 package org.apache.daffodil.processors.unparsers
 
+import org.apache.daffodil.schema.annotation.props.gen.TextZonedSignStyle
 import org.apache.daffodil.processors._
-import org.apache.daffodil.util.Maybe
-import org.apache.daffodil.util.Maybe._
-import org.apache.daffodil.cookers.EntityReplacer
-import org.apache.daffodil.exceptions.Assert
-import org.apache.daffodil.processors.parsers.NumberFormatFactoryBase
-import org.apache.daffodil.processors.parsers.ConvertTextNumberParserUnparserHelperBase
-
-case class ConvertTextCombinatorUnparser(
+import org.apache.daffodil.util.DecimalUtils
+import org.apache.daffodil.processors.parsers.ConvertZonedNumberParserUnparserHelperBase
+
+case class ConvertZonedCombinatorUnparser(
   rd: TermRuntimeData,
   valueUnparser: Unparser,
   converterUnparser: Unparser)
@@ -44,9 +41,9 @@ case class ConvertTextCombinatorUnparser(
   }
 }
 
-case class ConvertTextNumberUnparser[S](
-  helper: ConvertTextNumberParserUnparserHelperBase[S],
-  nff: NumberFormatFactoryBase[S],
+case class ConvertZonedNumberUnparser[S](
+  helper: ConvertZonedNumberParserUnparserHelperBase[S],
+  zonedSignStyle: TextZonedSignStyle,
   override val context: ElementRuntimeData)
   extends PrimUnparser
   with ToBriefXMLImpl {
@@ -56,36 +53,17 @@ case class ConvertTextNumberUnparser[S](
   override def toString = "to(xs:" + helper.xsdType + ")"
   override lazy val childProcessors = Nil
 
-  lazy val zeroRep: Maybe[String] = helper.zeroRepListRaw.headOption.map { zr =>
-    EntityReplacer { _.replaceForUnparse(zr) }
-  }
-
   override def unparse(state: UState): Unit = {
 
     val node = state.currentInfosetNode.asSimple
-    val value = node.dataValue
+    val value = node.dataValueAsString
 
     // The type of value should have the type of S, but type erasure makes this
     // difficult to assert. Could probably check this with TypeTags or Manifest
     // if we find this is not the case. Want something akin to:
     // Assert.invariant(value.isInstanceOf[S])
 
-    val strRep =
-      if (value == 0 && zeroRep.isDefined) {
-        zeroRep.get
-      } else {
-        // Needed because the DecimalFormat class of ICU will call
-        // doubleValue on scala's BigInt and BigDecimal because it
-        // doesn't recognize it as Java's BigInteger and BigDecimal.
-        // This caused large numbers to be truncated silently.
-        value match {
-          case bd: scala.math.BigDecimal => Assert.usageError("Received scala.math.BigDecimal, expected java.math.BigDecimal.")
-          case bi: scala.math.BigInt => Assert.usageError("Received scala.math.BigInt, expected java.math.BigInteger.")
-          case _ => // OK
-        }
-        val df = nff.getNumFormat(state)
-        df.get.format(value)
-      }
+    val strRep = DecimalUtils.zonedFromNumber(value, zonedSignStyle)
 
     node.overwriteDataValue(strRep)
   }
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/ZonedTextParsers.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/ZonedTextParsers.scala
index d300cd8..ceaac9a 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/ZonedTextParsers.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/ZonedTextParsers.scala
@@ -17,14 +17,501 @@
 
 package org.apache.daffodil.processors.parsers
 
+import org.apache.daffodil.schema.annotation.props.gen. { TextNumberCheckPolicy, TextNumberRounding, TextNumberRoundingMode, TextZonedSignStyle }
+import org.apache.daffodil.exceptions.ThrowsSDE
+import org.apache.daffodil.exceptions.Assert
+import org.apache.daffodil.exceptions.UnsuppressableException
+import org.apache.daffodil.util.Maybe
+import org.apache.daffodil.util.DecimalUtils
+import java.text.ParsePosition
+import com.ibm.icu.text.DecimalFormat
+import com.ibm.icu.text.DecimalFormatSymbols
+import org.apache.daffodil.util.MaybeDouble
+import org.apache.daffodil.util.MaybeDouble
+import java.lang.{ Number => JNumber }
+import java.math.{ BigDecimal => JBigDecimal, BigInteger => JBigInt }
+import org.apache.daffodil.infoset.DISimple
+import org.apache.daffodil.processors.Dynamic
 import org.apache.daffodil.processors.ElementRuntimeData
+import org.apache.daffodil.processors.ParseOrUnparseState
+import org.apache.daffodil.processors.Success
+import java.lang.{ Number => JNumber }
+import java.math.{ BigDecimal => JBigDecimal }
+import java.math.{ BigInteger => JBigInt }
+import org.apache.daffodil.processors.TermRuntimeData
 
-class ZonedTextNumberParser(override val context: ElementRuntimeData)
-  extends TextPrimParser {
+case class ConvertZonedCombinatorParser(
+  rd: TermRuntimeData,
+  valueParser: Parser,
+  converterParser: Parser)
+  extends CombinatorParser(rd) {
 
   override lazy val runtimeDependencies = Nil
 
+  override lazy val childProcessors = Seq(valueParser, converterParser)
+
+  def parse(start: PState): Unit = {
+    valueParser.parse1(start)
+    if (start.processorStatus ne Success) {
+      return
+    }
+    converterParser.parse1(start)
+  }
+}
+
+case class ConvertZonedNumberParser[S](
+  helper: ConvertZonedNumberParserUnparserHelperBase[S],
+  nff: ZonedFormatFactoryBase[S],
+  zonedSignStyle: TextZonedSignStyle,
+  override val context: ElementRuntimeData) extends TextPrimParser {
+  override lazy val runtimeDependencies = Nil
+
+  override def toString = "to(xs:" + helper.xsdType + ")"
+
   def parse(start: PState): Unit = {
-    context.notYetImplemented("Zoned Numbers")
+    val node: DISimple = start.simpleElement
+    val str = node.dataValueAsString
+
+    Assert.invariant(str != null) // worst case it should be empty string. But not null.
+    if (str == "") {
+      PE(start, "Convert to %s (for xs:%s): Cannot parse number from empty string", helper.prettyType, helper.xsdType)
+      return
+    }
+
+    // because of the way the zero rep regular expressions are generated, they
+    // will match either all or none of 'str', never part of it. Thus,
+    // findFirstIn() either matches and it's a zero rep, or it doesn't and it's
+    // not a zero
+    val numValue = {
+      val df = nff.getNumFormat(start)
+      val pos = new ParsePosition(0)
+      val num = try {
+        val decodedNum = DecimalUtils.zonedToNumber(str, zonedSignStyle)
+        df.get.parse(decodedNum, pos)
+      } catch {
+        case s: scala.util.control.ControlThrowable => throw s
+        case u: UnsuppressableException => throw u
+        case e: Exception => {
+          PE(start, "Convert to %s (for xs:%s): Parse of '%s' threw exception %s",
+            helper.prettyType, helper.xsdType, str, e)
+          return
+        }
+      }
+
+      // Verify that what was parsed was what was passed exactly in byte count.
+      // Use pos to verify all characters consumed & check for errors!
+      if (num == null || pos.getIndex != str.length) {
+        PE(start, "Convert to %s (for xs:%s): Unable to parse '%s' (using up all characters).",
+          helper.prettyType, helper.xsdType, str)
+        return
+      }
+
+      val numValue = {
+        if (helper.isInvalidRange(num)) {
+          PE(start, "Convert to %s (for xs:%s): Out of Range: '%s' converted to %s, is not in range for the type.",
+            helper.prettyType, helper.xsdType, str, num)
+          return
+        }
+
+        // convert to proper type
+        val asNumber = helper.getNum(num)
+        Assert.invariant(!asNumber.isInstanceOf[String])
+
+        // The following change was made because of the issues with the float
+        // adding a position of precision to the Number object.  At some point we
+        // will want to revert this back to the actual type but this is a quick fix
+        // for the issues we were having with the 0.003 vs 0.0030 error in test_DelimProp_05
+        //
+        //helper.getStringFormat(asNumber)
+        //
+        // Above was changed back into the actual number object.
+        asNumber
+      }
+      numValue
+    }
+
+    Assert.invariant(!numValue.isInstanceOf[String])
+    node.overwriteDataValue(numValue.asInstanceOf[JNumber])
+
+  }
+}
+
+abstract class ConvertZonedNumberParserUnparserHelperBase[S]() extends Serializable {
+  val xsdType: String
+  val prettyType: String
+
+  def getNum(s: Number): S
+  def isInt: Boolean
+  def isInvalidRange(n: java.lang.Number): Boolean
+  def getStringFormat(n: S): String
+}
+
+abstract class ConvertZonedIntegerNumberParserUnparserHelper[S]()
+  extends ConvertZonedNumberParserUnparserHelperBase[S]() {
+  override def isInt = true
+
+  override def getStringFormat(n: S): String = n.toString()
+
+  def isInvalidRange(n: java.lang.Number): Boolean = {
+    //
+    // Note: Scala has no class analogous to java.lang.Number. There's no common
+    // base class above its number types (as there isn't above the Java *primitive* number types.)
+    //
+    // We're being handed here a java 'boxed' number type, and those have common parent Number.
+    //
+    // println("number's actual type is: " + n.getClass.getName)
+    //
+    // This method only for things that fit in range of a Long. (i.e., not unbounded size Integer, and not unsignedLong
+    // Nevertheless, if invalid data much too long for the real numeric type is what is found in the data
+    // then a java BigInteger (or maybe even BigDecimal might get passed here.
+    //
+    // The only thing we can check is whether there is conversion to a long available.
+    // e.g., like this: Assert.invariant(n.isInstanceOf[{ def longValue : Long}])
+    // But that's eliminated by erasure, so we'll just do without.
+    //
+    val l = n.longValue
+
+    // check for overflow/underflow.
+    val orig = new JBigDecimal(n.toString)
+    val newl = new JBigDecimal(l)
+    if (orig.compareTo(newl) != 0) {
+      true
+    } else {
+      l < min || l > max
+    }
+  }
+  def min: Long
+  def max: Long
+}
+
+abstract class ConvertZonedFloatingPointNumberParserUnparserHelper[S]()
+  extends ConvertZonedNumberParserUnparserHelperBase[S]() {
+  override def isInt = false
+  override def getStringFormat(n: S): String = {
+
+    //val trailingZeroes = """0*(?!<[1-9])$"""
+    val trailingZeroes = """(?<=[1-9])(0*)$""".r
+    val trailingZeroesBeforeExponent = """(?<=[1-9])(0*?)(?=E.*)""".r
+
+    val nAsStr = n.toString()
+
+    if (nAsStr.contains("E") || nAsStr.contains("e")) {
+      // Exponent
+      trailingZeroesBeforeExponent.replaceAllIn(nAsStr, "")
+    } else {
+      trailingZeroes.replaceAllIn(nAsStr, "")
+    }
+
+    nAsStr
+  }
+
+}
+
+case class ConvertZonedIntegerParserUnparserHelper[S]()
+  extends ConvertZonedIntegerNumberParserUnparserHelper[JBigInt]() {
+
+  override def getNum(num: Number) = new JBigInt(num.toString)
+  override val xsdType = "integer"
+  override val prettyType = "Unlimited Size Integer"
+  override def isInvalidRange(n: java.lang.Number): Boolean = false
+  def min = -1 // ignored
+  def max = -1 // ignored
+}
+
+case class ConvertZonedNonNegativeIntegerParserUnparserHelper[S]()
+  extends ConvertZonedIntegerNumberParserUnparserHelper[JBigInt]() {
+
+  override def getNum(num: Number) = new JBigInt(num.toString)
+  override val xsdType = "nonNegativeInteger"
+  override val prettyType = "Unlimited Size Non Negative Integer"
+  override def isInvalidRange(n: java.lang.Number): Boolean = {
+    val value = new JBigDecimal(n.toString())
+    val isNegative = value.signum == -1
+    if (isNegative) return true
+    false
+  }
+  def min = -1 // ignored
+  def max = -1 // ignored
+}
+
+case class ConvertZonedLongParserUnparserHelper[S]()
+  extends ConvertZonedIntegerNumberParserUnparserHelper[Long]() {
+
+  override def getNum(num: Number) = num.longValue
+  override val xsdType = "long"
+  override val prettyType = "Long Integer"
+  val min = Long.MinValue
+  val max = Long.MaxValue
+}
+
+case class ConvertZonedIntParserUnparserHelper[S]()
+  extends ConvertZonedIntegerNumberParserUnparserHelper[Int]() {
+
+  override def getNum(num: Number) = num.intValue
+  override val xsdType = "int"
+  override val prettyType = "Integer"
+  val min = Int.MinValue.toLong
+  val max = Int.MaxValue.toLong
+}
+
+case class ConvertZonedShortParserUnparserHelper[S]()
+  extends ConvertZonedIntegerNumberParserUnparserHelper[Short]() {
+
+  override def getNum(num: Number) = num.shortValue
+  override val xsdType = "short"
+  override val prettyType = "Short Integer"
+  val min = Short.MinValue.toLong
+  val max = Short.MaxValue.toLong
+}
+
+case class ConvertZonedByteParserUnparserHelper[S]()
+  extends ConvertZonedIntegerNumberParserUnparserHelper[Byte]() {
+
+  override def getNum(num: Number) = num.byteValue
+  override val xsdType = "byte"
+  override val prettyType = "Byte"
+  val min = Byte.MinValue.toLong
+  val max = Byte.MaxValue.toLong
+}
+
+case class ConvertZonedUnsignedLongParserUnparserHelper[S]()
+  extends ConvertZonedIntegerNumberParserUnparserHelper[JBigInt]() {
+
+  override def getNum(num: Number) = new JBigInt(num.toString)
+  override val xsdType = "unsignedLong"
+  override val prettyType = "Unsigned Long"
+  override def isInvalidRange(jn: java.lang.Number) = {
+    jn match {
+      case n: JBigInt => {
+        n.compareTo(JBigInt.ZERO) < 0 || n.compareTo(JBigInt.ONE.shiftLeft(64)) >= 0
+      }
+      case _ => {
+        val n = jn.longValue()
+        n < 0 // note: the other side of the check is inherently ok since a Long must be smaller than an unsignedLong.
+      }
+    }
   }
+  val min = 0.toLong
+  val max = -1.toLong // unused.
+}
+
+case class ConvertZonedUnsignedIntParserUnparserHelper[S]()
+  extends ConvertZonedIntegerNumberParserUnparserHelper[Long]() {
+
+  override def getNum(num: Number) = num.longValue
+  override val xsdType = "unsignedInt"
+  override val prettyType = "Unsigned Integer"
+  val min = 0L
+  val max = (1L << 32) - 1L
+}
+
+case class ConvertZonedUnsignedShortParserUnparserHelper[S]()
+  extends ConvertZonedIntegerNumberParserUnparserHelper[Int]() {
+
+  override def getNum(num: Number) = num.intValue
+  override val xsdType = "unsignedShort"
+  override val prettyType = "Unsigned Short"
+  val min = 0L
+  val max = (1L << 16) - 1L
+}
+
+case class ConvertZonedUnsignedByteParserUnparserHelper[S]()
+  extends ConvertZonedIntegerNumberParserUnparserHelper[Short]() {
+
+  override def getNum(num: Number) = num.shortValue
+  override val xsdType = "unsignedByte"
+  override val prettyType = "Unsigned Byte"
+  val min = 0L
+  val max = (1L << 8) - 1L
+}
+
+case class ConvertZonedDecimalParserUnparserHelper[S]()
+  extends ConvertZonedFloatingPointNumberParserUnparserHelper[JBigDecimal]() {
+
+  override def getNum(num: Number) = new JBigDecimal(num.toString)
+  override val xsdType = "decimal"
+  override val prettyType = "Unlimited Size Decimal"
+  override def isInvalidRange(n: java.lang.Number): Boolean = false
+
+  override def getStringFormat(n: JBigDecimal): String = {
+    n.toPlainString()
+  }
+}
+
+case class ConvertZonedDoubleParserUnparserHelper[S]()
+  extends ConvertZonedFloatingPointNumberParserUnparserHelper[Double]() {
+
+  val MAX_VALUE = new JBigDecimal(Double.MaxValue)
+  val MIN_VALUE = new JBigDecimal(Double.MinValue)
+
+  override def getNum(num: Number) = num.doubleValue
+  override val xsdType = "double"
+  override val prettyType = "Double"
+  def isInvalidRange(n: java.lang.Number): Boolean = {
+    val d = n.doubleValue() // This can truncate the number and void range checking
+    val bd = new JBigDecimal(n.toString)
+    (d.isNaN || bd.compareTo(MIN_VALUE) < 0 || bd.compareTo(MAX_VALUE) > 0)
+  }
+}
+
+case class ConvertZonedFloatParserUnparserHelper[S]()
+  extends ConvertZonedFloatingPointNumberParserUnparserHelper[Float]() {
+
+  val MAX_VALUE = new JBigDecimal(Float.MaxValue)
+  val MIN_VALUE = new JBigDecimal(Float.MinValue)
+
+  override def getNum(num: Number) = num.floatValue
+  override val xsdType = "float"
+  override val prettyType = "Float"
+  def isInvalidRange(n: java.lang.Number): Boolean = {
+    val f = n.floatValue() // This can truncated the number and void range checking
+    val bd = new JBigDecimal(n.toString)
+    (f.isNaN || bd.compareTo(MIN_VALUE) < 0 || bd.compareTo(MAX_VALUE) > 0)
+  }
+}
+
+abstract class ZonedFormatFactoryBase[S](parserHelper: ConvertZonedNumberParserUnparserHelperBase[S]) extends Serializable {
+
+  protected def checkUnique(
+    context: ThrowsSDE) = {
+
+    import scala.collection.mutable.{ HashMap, MultiMap, Set }
+
+    val mm = new HashMap[String, Set[String]] with MultiMap[String, String]
+
+    val dupes = mm.filter { case (k, s) => s.size > 1 }
+    val dupeStrings = dupes.map {
+      case (k, s) =>
+        "Non-distinct property '%s' found in: %s".format(k, s.mkString(", "))
+    }
+    context.schemaDefinitionUnless(dupeStrings.size == 0, dupeStrings.mkString("\n"))
+  }
+
+  protected def generateNumFormat(
+    checkPolicy: TextNumberCheckPolicy,
+    pattern: String,
+    rounding: TextNumberRounding,
+    roundingMode: Maybe[TextNumberRoundingMode],
+    roundingIncrement: MaybeDouble) = {
+
+    val dfs = new DecimalFormatSymbols()
+
+    val df = new DecimalFormat(pattern, dfs)
+
+    val cp = checkPolicy match {
+      case TextNumberCheckPolicy.Strict => true
+      case TextNumberCheckPolicy.Lax => false
+    }
+    df.setParseStrict(cp)
+
+    rounding match {
+      case TextNumberRounding.Pattern => {
+        df.setRoundingMode(JBigDecimal.ROUND_HALF_EVEN)
+      }
+      case TextNumberRounding.Explicit => {
+        val rm = roundingMode.get match {
+          case TextNumberRoundingMode.RoundCeiling => JBigDecimal.ROUND_CEILING
+          case TextNumberRoundingMode.RoundFloor => JBigDecimal.ROUND_FLOOR
+          case TextNumberRoundingMode.RoundDown => JBigDecimal.ROUND_DOWN
+          case TextNumberRoundingMode.RoundUp => JBigDecimal.ROUND_UP
+          case TextNumberRoundingMode.RoundHalfEven => JBigDecimal.ROUND_HALF_EVEN
+          case TextNumberRoundingMode.RoundHalfDown => JBigDecimal.ROUND_HALF_DOWN
+          case TextNumberRoundingMode.RoundHalfUp => JBigDecimal.ROUND_HALF_UP
+          case TextNumberRoundingMode.RoundUnnecessary => JBigDecimal.ROUND_UNNECESSARY
+        }
+        df.setRoundingMode(rm)
+        df.setRoundingIncrement(roundingIncrement.get)
+      }
+    }
+
+    if (parserHelper.isInt) {
+      df.setMaximumFractionDigits(0)
+      df.setDecimalSeparatorAlwaysShown(false)
+      df.setParseIntegerOnly(true)
+    }
+
+    df
+  }
+
+  protected def getRoundingIncrement(roundingInc: Double, context: ThrowsSDE): Double = {
+    context.schemaDefinitionUnless(roundingInc >= 0, "textNumberRoundingIncrement cannot be negative")
+    roundingInc
+  }
+
+  // as per ICU4J documentation, "DecimalFormat objects are not
+  // synchronized. Multiple threads should not access one formatter
+  // concurrently."
+  def getNumFormat(state: ParseOrUnparseState): ThreadLocal[DecimalFormat]
+
+}
+
+//
+// TODO: Complexity - why do we need both Static and Dynamic variants of this?
+// CachedDynamic hides this distinction (or should), as does CompiledExpression underneath that.
+
+class ZonedFormatFactoryStatic[S](context: ThrowsSDE,
+  parserHelper: ConvertZonedNumberParserUnparserHelperBase[S],
+  checkPolicy: TextNumberCheckPolicy,
+  pattern: String,
+  rounding: TextNumberRounding,
+  roundingMode: Maybe[TextNumberRoundingMode],
+  roundingIncrement: MaybeDouble)
+  extends ZonedFormatFactoryBase[S](parserHelper) {
+
+  val roundingInc: MaybeDouble = if (roundingIncrement.isEmpty) MaybeDouble.Nope else MaybeDouble { getRoundingIncrement(roundingIncrement.value, context) }
+
+  checkUnique(
+    context)
+
+  @transient lazy val numFormat = new ThreadLocal[DecimalFormat] {
+    override def initialValue() = {
+      generateNumFormat(
+        checkPolicy,
+        pattern,
+        rounding,
+        roundingMode,
+        roundingInc)
+    }
+  }
+
+  def getNumFormat(state: ParseOrUnparseState): ThreadLocal[DecimalFormat] = {
+    numFormat
+  }
+}
+
+class ZonedFormatFactoryDynamic[S](staticContext: ThrowsSDE,
+  parserHelper: ConvertZonedNumberParserUnparserHelperBase[S],
+  checkPolicy: TextNumberCheckPolicy,
+  pattern: String,
+  rounding: TextNumberRounding,
+  roundingMode: Maybe[TextNumberRoundingMode],
+  roundingIncrement: MaybeDouble)
+  extends ZonedFormatFactoryBase[S](parserHelper)
+  with Dynamic {
+
+  checkUnique(staticContext)
+
+  val roundingInc = if (roundingIncrement.isEmpty) MaybeDouble.Nope else MaybeDouble { getRoundingIncrement(roundingIncrement.value, staticContext) }
+
+  def getNumFormat(state: ParseOrUnparseState): ThreadLocal[DecimalFormat] = {
+
+    checkUnique(state)
+
+    val generatedNumFormat =
+      generateNumFormat(
+        checkPolicy,
+        pattern,
+        rounding,
+        roundingMode,
+        roundingInc)
+
+    val numFormat = new ThreadLocal[DecimalFormat] {
+      override def initialValue() = {
+        generatedNumFormat
+      }
+    }
+
+    numFormat
+  }
+
 }