blob: e4c417cd66a7620dfce05330de9c9a90871426b0 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.daffodil.processors.unparsers
import java.lang.{ Number => JNumber }
import java.math.{ BigInteger => JBigInteger, BigDecimal => JBigDecimal }
import org.apache.daffodil.dpath.NodeInfo
import org.apache.daffodil.exceptions.Assert
import org.apache.daffodil.io.DataOutputStream
import org.apache.daffodil.io.FormatInfo
import org.apache.daffodil.processors.ElementRuntimeData
import org.apache.daffodil.processors.Evaluatable
import org.apache.daffodil.processors.ParseOrUnparseState
import org.apache.daffodil.util.Maybe._
import org.apache.daffodil.infoset.DataValue.DataValuePrimitiveNullable
trait PackedBinaryConversion {
def fromBigInteger(bigInt: JBigInteger, nBits: Int): Array[Byte]
}
abstract class PackedBinaryBaseUnparser(
override val context: ElementRuntimeData)
extends PrimUnparser
with PackedBinaryConversion {
override lazy val runtimeDependencies: Vector[Evaluatable[AnyRef]] = Vector()
protected def getBitLength(s: ParseOrUnparseState): Int
def putNumber(dos: DataOutputStream, number: JNumber, nBits: Int, finfo: FormatInfo): Boolean
def getNumberToPut(state: UState): JNumber = {
val node = state.currentInfosetNode.asSimple
// Packed decimal calendars use the convert combinator which sets the string value of the calendar
// - using dataValue would give the Calendar value rather than that string. Since the Calendar value
// cannot be cast as a JNumber we need to use dataValueAsString and convert it to a JBigInteger.
// With packed numbers, dataValue is already a number so just use that.
val nodeValue: DataValuePrimitiveNullable =
node.erd.optPrimType.get match {
case NodeInfo.Date | NodeInfo.DateTime | NodeInfo.Time => new JBigInteger(node.dataValueAsString)
case _ => node.dataValue
}
val value = nodeValue.getNumber
value
}
override def unparse(state: UState): Unit = {
val nBits = getBitLength(state)
val value = getNumberToPut(state)
val dos = state.dataOutputStream
val res = putNumber(dos, value, nBits, state)
if (!res) {
Assert.invariant(dos.maybeRelBitLimit0b.isDefined)
UnparseError(One(state.schemaFileLocation), One(state.currentLocation), "Insufficient space to unparse element %s, required %s bits, but only %s were available.",
context.dpathElementCompileInfo.namedQName.toPrettyString, nBits, dos.maybeRelBitLimit0b.get)
}
}
}
abstract class PackedBinaryDecimalBaseUnparser(
e: ElementRuntimeData,
binaryDecimalVirtualPoint: Int)
extends PackedBinaryBaseUnparser(e) {
override def getNumberToPut(ustate: UState): JNumber = {
val number = super.getNumberToPut(ustate)
val bigDec = number.asInstanceOf[JBigDecimal]
if (bigDec.movePointRight(binaryDecimalVirtualPoint).scale != 0) {
e.schemaDefinitionError("Decimal point of number '%s' does not match the binaryVirtualDecmialPoint: %d", bigDec, binaryDecimalVirtualPoint)
}
bigDec.unscaledValue
}
override def putNumber(dos: DataOutputStream, number: JNumber, nBits: Int, finfo: FormatInfo): Boolean = {
val packedNum = fromBigInteger(number.asInstanceOf[JBigInteger], nBits)
dos.putByteArray(packedNum, packedNum.length * 8, finfo)
}
}
abstract class PackedBinaryIntegerBaseUnparser(
e: ElementRuntimeData)
extends PackedBinaryBaseUnparser(e) {
override def getNumberToPut(ustate: UState): JNumber = {
val number = super.getNumberToPut(ustate)
val bigInt = number.isInstanceOf[JBigInteger] match {
case true => number.asInstanceOf[JBigInteger]
case false => new JBigInteger(number.toString)
}
bigInt
}
override def putNumber(dos: DataOutputStream, number: JNumber, nBits: Int, finfo: FormatInfo): Boolean = {
val packedNum = fromBigInteger(number.asInstanceOf[JBigInteger], nBits)
dos.putByteArray(packedNum, packedNum.length * 8, finfo)
}
}