Implemented platform specific wrapper classes for BigDecimal, Date, File and UUID

This commit is contained in:
dankito 2020-07-11 12:22:52 +02:00
parent 2281d1758d
commit 1bef1904f9
19 changed files with 441 additions and 3 deletions

View File

@ -0,0 +1,21 @@
package net.dankito.utils.multiplatform
expect fun Collection<BigDecimal>.sum(): BigDecimal
expect class BigDecimal {
companion object {
val Zero: BigDecimal
}
constructor(decimal: String)
constructor(double: Double)
fun format(pattern: String): String
}

View File

@ -0,0 +1,20 @@
package net.dankito.utils.multiplatform
fun Date.format(format: DateFormatter): String {
return format.format(this)
}
fun Date.format(pattern: String): String {
return this.format(DateFormatter(pattern))
}
expect class Date(millisSinceEpoch: Long) {
constructor()
val millisSinceEpoch: Long
}

View File

@ -0,0 +1,14 @@
package net.dankito.utils.multiplatform
enum class DateFormatStyle {
Short,
Medium,
Long,
Full
}

View File

@ -0,0 +1,13 @@
package net.dankito.utils.multiplatform
expect class DateFormatter constructor(pattern: String) {
constructor(dateStyle: DateFormatStyle)
constructor(dateStyle: DateFormatStyle, timeStyle: DateFormatStyle)
fun format(date: Date): String
}

View File

@ -0,0 +1,19 @@
package net.dankito.utils.multiplatform
expect class File(path: String) {
constructor(folder: File, filename: String)
// have to specify it as method as property would conflict with java.io.File's getAbsolutePath
fun getAbsolutePath(): String
val filename: String
val fileExtension: String
fun mkdirs(): Boolean
}

View File

@ -0,0 +1,12 @@
package net.dankito.utils.multiplatform
expect class UUID {
companion object {
fun random(): String
}
}

View File

@ -1,17 +1,18 @@
package net.dankito.utils.multiplatform.log
import com.soywiz.klock.DateTime
import net.dankito.utils.multiplatform.Date
import net.dankito.utils.multiplatform.DateFormatter
open class ConsoleLogger(name: String) : LoggerBase(name) {
companion object {
private val DateFormat = com.soywiz.klock.DateFormat.invoke("HH:mm:ss.SSS")
private val DateFormatter = DateFormatter("HH:mm:ss.SSS")
}
override fun log(level: LogLevel, message: String) {
println("${DateTime.nowLocal().toString(DateFormat)} $level $name - $message")
println("${DateFormatter.format(Date())} $level $name - $message")
}
}

View File

@ -0,0 +1,51 @@
package net.dankito.utils.multiplatform
import platform.Foundation.NSCoder
import platform.Foundation.NSDecimalNumber
import platform.Foundation.NSNumberFormatter
import platform.Foundation.NSOrderedSame
fun NSDecimalNumber.toBigDecimal(): BigDecimal {
return BigDecimal(this.stringValue) // TODO: find a better way than double string conversion
}
actual fun Collection<BigDecimal>.sum(): BigDecimal {
return this.fold(NSDecimalNumber.zero) { acc, e -> acc.decimalNumberByAdding(e) }.toBigDecimal()
}
actual class BigDecimal actual constructor(decimal: String) : NSDecimalNumber(NSCoder()) {
actual companion object {
actual val Zero = BigDecimal("0") // TODO: is this correct?
}
actual constructor(double: Double) : this(NSDecimalNumber(double).stringValue)
init {
super.decimalNumberByAdding(NSDecimalNumber(decimal))
}
actual fun format(pattern: String): String {
val formatter = NSNumberFormatter()
formatter.positiveFormat = pattern
formatter.negativeFormat = pattern
return formatter.stringFromNumber(this) ?: ""
}
override fun isEqual(`object`: Any?): Boolean {
if (`object` is BigDecimal) {
return this.compare(`object`) == NSOrderedSame
}
return super.equals(`object`)
}
}

View File

@ -0,0 +1,25 @@
package net.dankito.utils.multiplatform
import platform.Foundation.*
fun NSTimeInterval.toMillis(): Long {
return this.toLong() * 1000
}
actual class Date actual constructor(millisSinceEpoch: Long)
: NSDate(timeIntervalSinceReferenceDate = ((millisSinceEpoch - DiffBetweenEpochTimeAndReferenceDate) / 1000).toDouble()) { // TODO: does this work?
companion object {
val DiffBetweenEpochTimeAndReferenceDate = (NSDate.timeIntervalSinceReferenceDate - NSTimeIntervalSince1970).toMillis()
}
actual constructor() : this(NSDate().timeIntervalSince1970.toMillis())
actual val millisSinceEpoch: Long
get() = timeIntervalSince1970.toMillis()
}

View File

@ -0,0 +1,40 @@
package net.dankito.utils.multiplatform
import platform.Foundation.*
fun DateFormatStyle.convert(): ULong {
return when (this) {
DateFormatStyle.Short -> NSDateFormatterShortStyle
DateFormatStyle.Medium -> NSDateFormatterMediumStyle
DateFormatStyle.Long -> NSDateFormatterLongStyle
DateFormatStyle.Full -> NSDateFormatterFullStyle
}
}
actual class DateFormatter actual constructor(val pattern: String): NSDateFormatter() {
actual constructor(dateStyle: DateFormatStyle) : this(NSDateFormatter().apply {
this.dateStyle = dateStyle.convert()
}.dateFormat) // TODO: does this work?
actual constructor(dateStyle: DateFormatStyle, timeStyle: DateFormatStyle) : this(NSDateFormatter().apply {
this.dateStyle = dateStyle.convert()
this.timeStyle = timeStyle.convert()
}.dateFormat) // TODO: does this work?
init {
this.dateFormat = pattern
this.timeZone = NSTimeZone.localTimeZone // TODO: needed?
}
actual fun format(date: Date): String {
return stringFromDate(date)
}
}

View File

@ -0,0 +1,29 @@
package net.dankito.utils.multiplatform
import platform.Foundation.NSFileManager
import platform.Foundation.NSURL
import platform.Foundation.lastPathComponent
actual class File actual constructor(path: String) : NSURL(fileURLWithPath = path) {
actual constructor(folder: File, filename: String)
: this(NSURL(string = filename, relativeToURL = folder).absoluteString ?: "") // TODO: or use 'fileURLWithPath'?
actual fun getAbsolutePath(): String {
return absoluteString ?: absoluteURL?.absoluteString ?: path ?: ""
}
actual val filename: String
get() = lastPathComponent ?: ""
actual val fileExtension: String
get() = filename.substringAfterLast('.', "")
actual fun mkdirs(): Boolean {
return NSFileManager.defaultManager.createDirectoryAtURL(this, true, null, null)
}
}

View File

@ -0,0 +1,16 @@
package net.dankito.utils.multiplatform
import platform.Foundation.NSUUID
actual class UUID {
actual companion object {
actual fun random(): String {
return NSUUID().UUIDString
}
}
}

View File

@ -0,0 +1,54 @@
package net.dankito.utils.multiplatform
fun java.math.BigDecimal.toBigDecimal(): BigDecimal {
return BigDecimal(this.toPlainString()) // TODO: find a better way than double string conversion
}
actual fun Collection<BigDecimal>.sum(): BigDecimal {
return this.fold(java.math.BigDecimal.ZERO) { acc, e -> (acc + e) }.toBigDecimal()
}
actual class BigDecimal actual constructor(decimal: String) : java.math.BigDecimal(decimal) {
actual companion object {
actual val Zero =
BigDecimal("0") // TODO: is this correct?
}
internal constructor() : this("0") // for object deserializers
actual constructor(double: Double) : this(java.math.BigDecimal.valueOf(double).toPlainString()) // for object deserializers
actual fun format(pattern: String): String {
return String.format(pattern, this)
}
override fun equals(other: Any?): Boolean {
if (other is BigDecimal) {
return this.compareTo(other) == 0
}
return super.equals(other)
}
/* TODO: where are these methods coming from? */
override fun toByte(): Byte {
return 0 // will never be called; where is this method coming from?
}
override fun toChar(): Char {
return 0.toChar() // will never be called; where is this method coming from?
}
override fun toShort(): Short {
return 0 // will never be called; where is this method coming from?
}
}

View File

@ -0,0 +1,12 @@
package net.dankito.utils.multiplatform
actual class Date actual constructor(millisSinceEpoch: Long) : java.util.Date(millisSinceEpoch) {
actual constructor() : this(System.currentTimeMillis())
actual val millisSinceEpoch: Long
get() = time
}

View File

@ -0,0 +1,30 @@
package net.dankito.utils.multiplatform
import java.text.DateFormat
import java.text.SimpleDateFormat
fun DateFormatStyle.convert(): Int {
return when (this) {
DateFormatStyle.Short -> DateFormat.SHORT
DateFormatStyle.Medium -> DateFormat.MEDIUM
DateFormatStyle.Long -> DateFormat.LONG
DateFormatStyle.Full -> DateFormat.FULL
}
}
actual class DateFormatter actual constructor(val pattern: String): SimpleDateFormat(pattern) {
actual constructor(dateStyle: DateFormatStyle)
: this((DateFormat.getDateInstance(dateStyle.convert()) as? SimpleDateFormat)?.toPattern() ?: "")
actual constructor(dateStyle: DateFormatStyle, timeStyle: DateFormatStyle)
: this((DateFormat.getDateTimeInstance(dateStyle.convert(), timeStyle.convert()) as? SimpleDateFormat)?.toPattern() ?: "")
actual fun format(date: Date): String {
return super.format(date)
}
}

View File

@ -0,0 +1,6 @@
package net.dankito.utils.multiplatform
fun java.util.Date.toDate(): Date {
return Date(this.time)
}

View File

@ -0,0 +1,35 @@
package net.dankito.utils.multiplatform
import java.io.File
fun File.toFile(): net.dankito.utils.multiplatform.File {
return net.dankito.utils.multiplatform.File(this.absolutePath)
}
actual class File actual constructor(path: String) : File(path) {
actual constructor(folder: net.dankito.utils.multiplatform.File, filename: String)
: this(File(folder, filename).absolutePath)
internal constructor() : this("") // for object deserializers
actual override fun getAbsolutePath(): String {
return super.getAbsolutePath()
}
actual val filename: String
get() = super.getName()
actual val fileExtension: String
get() = this.extension
actual override fun mkdirs(): Boolean {
return super.mkdirs()
}
}

View File

@ -0,0 +1,16 @@
package net.dankito.utils.multiplatform
import java.util.UUID
actual class UUID {
actual companion object {
actual fun random(): String {
return UUID.randomUUID().toString()
}
}
}

View File

@ -0,0 +1,24 @@
package net.dankito.utils.multiplatform.log
actual class DefaultLoggerFactory {
actual fun createDefaultLoggerFactory(): ILoggerFactory {
if (isClassAvailable("org.slf4j.Logger")) {
return Slf4jLoggerFactory()
}
return LogToConsoleLoggerFactory()
}
private fun isClassAvailable(qualifiedClassName: String): Boolean {
try {
Class.forName(qualifiedClassName)
return true
} catch (ignored: Exception) { }
return false
}
}