Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enforce immutability and insertion iteration order properties of ZplCommand.parameters #20

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions src/main/kotlin/com/sainsburys/k2zpl/command/BarCode.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,21 @@ internal data class BarCode(
val height: Int,
val line: Int,
val lineAbove: ZplYesNo
) : ZplCommand {
init {
require(height in 1..32000) { "Height must be between 1 and 32000" }
require(line in 1..7) { "Line thickness must be between 1 and 7" }
}

override val command: CharSequence = "^B1"
override val parameters: Map<CharSequence, Any?> = addParameters(
) : ZplCommand(
parameters = listOf(
"o" to orientation.code,
"c" to checkDigit.toString(),
"h" to height,
"l" to line,
"la" to lineAbove.toString()
)
) {
init {
require(height in 1..32000) { "Height must be between 1 and 32000" }
require(line in 1..7) { "Line thickness must be between 1 and 7" }
}

override val command: CharSequence = "^B1"
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
package com.sainsburys.k2zpl.command

internal data class CustomCommand(override val command: CharSequence) : ZplCommand
internal data class CustomCommand(override val command: CharSequence) : ZplCommand()
2 changes: 1 addition & 1 deletion src/main/kotlin/com/sainsburys/k2zpl/command/EndFormat.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.sainsburys.k2zpl.command

import com.sainsburys.k2zpl.builder.ZplBuilder

internal data object EndFormat : ZplCommand {
internal data object EndFormat : ZplCommand() {
override val command: CharSequence = "^XZ"
}

Expand Down
17 changes: 9 additions & 8 deletions src/main/kotlin/com/sainsburys/k2zpl/command/FieldBlock.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,15 @@ internal data class FieldBlock(
val lineSpacing: Int,
val alignment: ZplTextAlignment,
val hangingIndent: Int
) : ZplCommand {
) : ZplCommand(
parameters = listOf(
"w" to width,
"l" to maxLines,
"s" to lineSpacing,
"a" to alignment.code,
"h" to hangingIndent
)
) {
init {
require(width in 1..32000) { "Width must be between 1 and 32000" }
require(maxLines in 1..9999) { "Lines must be between 1 and 9999" }
Expand All @@ -18,13 +26,6 @@ internal data class FieldBlock(
}

override val command: CharSequence = "^FB"
override val parameters: Map<CharSequence, Any?> = addParameters(
"w" to width,
"l" to maxLines,
"s" to lineSpacing,
"a" to alignment.code,
"h" to hangingIndent
)
}

/**
Expand Down
6 changes: 4 additions & 2 deletions src/main/kotlin/com/sainsburys/k2zpl/command/FieldData.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package com.sainsburys.k2zpl.command

import com.sainsburys.k2zpl.builder.ZplBuilder

internal data class FieldData(val data: String) : ZplCommand {
internal data class FieldData(val data: String) : ZplCommand(
parameters = listOf("d" to data)
) {

override val command: CharSequence = "^FD"
override val parameters: Map<CharSequence, Any?> = addParameters("d" to data)

override fun build(stringBuilder: StringBuilder): StringBuilder {
return stringBuilder
Expand Down
13 changes: 7 additions & 6 deletions src/main/kotlin/com/sainsburys/k2zpl/command/FieldOrigin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,19 @@ internal data class FieldOrigin(
val x: Int,
val y: Int,
val justification: ZplJustification
) : ZplCommand {
) : ZplCommand(
parameters = listOf(
"x" to x,
"y" to y,
"j" to justification
)
) {
init {
require(x in 0..32000) { "x must be within 0 to 32000" }
require(y in 0..32000) { "y must be within 0 to 32000" }
}

override val command: CharSequence = "^FO"
override val parameters: Map<CharSequence, Any?> = addParameters(
"x" to x,
"y" to y,
"j" to justification
)
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.sainsburys.k2zpl.command

import com.sainsburys.k2zpl.builder.ZplBuilder

internal data object FieldSeparator : ZplCommand {
internal data object FieldSeparator : ZplCommand() {
override val command: CharSequence = "^FS"
}

Expand Down
9 changes: 5 additions & 4 deletions src/main/kotlin/com/sainsburys/k2zpl/command/Font.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,17 @@ internal data class Font(
val orientation: ZplFieldOrientation,
val height: Int,
val width: Int
) :
ZplCommand {
) : ZplCommand(
parameters = listOf(
"o" to orientation, "h" to height, "w" to width
)
) {
init {
require(height in 10..32000) { "Height must be between 10 and 32000" }
require(width in 10..32000) { "Width must be between 10 and 32000" }
}

override val command: CharSequence = "^A${font}"
override val parameters: Map<CharSequence, Any?> =
addParameters("o" to orientation, "h" to height, "w" to width)
}


Expand Down
16 changes: 8 additions & 8 deletions src/main/kotlin/com/sainsburys/k2zpl/command/GraphicBox.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,14 @@ internal data class GraphicBox(
val thickness: Int = 1,
val color: ZplLineColor = ZplLineColor.BLACK,
val rounding: Int = 0
) : ZplCommand {
) : ZplCommand(
parameters = listOf(
"w" to width,
"h" to height,
"t" to thickness, "c" to color.code,
"r" to rounding
)
) {
init {
require(width in 1..32000) { "Width must be between 1 and 32000" }
require(height in 1..32000) { "Height must be between 1 and 32000" }
Expand All @@ -18,13 +25,6 @@ internal data class GraphicBox(
}

override val command: CharSequence = "^GB"
override val parameters: Map<CharSequence, Any?> =
addParameters(
"w" to width,
"h" to height,
"t" to thickness, "c" to color.code,
"r" to rounding
)
}

/**
Expand Down
17 changes: 9 additions & 8 deletions src/main/kotlin/com/sainsburys/k2zpl/command/GraphicField.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,22 @@ internal data class GraphicField(
val totalBytes: Int,
val rowBytes: Int,
val data: String
) : ZplCommand {
) : ZplCommand(
parameters = listOf(
"f" to format,
"db" to dataBytes,
"tb" to totalBytes,
"rb" to rowBytes,
"d" to data
)
) {
init {
require(dataBytes in 1..999999) { "Data bytes must be between 1 and 999999" }
require(totalBytes in 1..999999) { "Total bytes must be between 1 and 999999" }
require(rowBytes in 1..32000) { "Row bytes must be between 1 and 32000" }
}

override val command: CharSequence = "^GF"
override val parameters: Map<CharSequence, Any?> = addParameters(
"f" to format,
"db" to dataBytes,
"tb" to totalBytes,
"rb" to rowBytes,
"d" to data
)
}

/**
Expand Down
5 changes: 3 additions & 2 deletions src/main/kotlin/com/sainsburys/k2zpl/command/LabelHome.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ package com.sainsburys.k2zpl.command

import com.sainsburys.k2zpl.builder.ZplBuilder

internal data class LabelHome(val x: Int, val y: Int) : ZplCommand {
internal data class LabelHome(val x: Int, val y: Int) : ZplCommand(
parameters = listOf("x" to x, "y" to y)
) {
override val command: CharSequence = "^LH"
override val parameters: Map<CharSequence, Any?> = addParameters("x" to x, "y" to y)
}

/**
Expand Down
5 changes: 3 additions & 2 deletions src/main/kotlin/com/sainsburys/k2zpl/command/LabelLength.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ package com.sainsburys.k2zpl.command

import com.sainsburys.k2zpl.builder.ZplBuilder

internal data class LabelLength(val length: Int) : ZplCommand {
internal data class LabelLength(val length: Int) : ZplCommand(
parameters = listOf("l" to length)
) {
init {
require(length in 1..32000) { "Length must be between 1 and 32000" }
}

override val command: CharSequence = "^LL"
override val parameters: Map<CharSequence, Any?> = addParameters("l" to length)
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package com.sainsburys.k2zpl.command
* Allow for lazy evaluation of a ZplCommand. This works by
* passing in a block that will be executed when [build] is called.
*/
internal class LazyCommand(private val commandBlock: () -> ZplCommand) : ZplCommand {
internal class LazyCommand(private val commandBlock: () -> ZplCommand) : ZplCommand() {
override val command: CharSequence get() = ""

override fun build(stringBuilder: StringBuilder): StringBuilder {
Expand Down
6 changes: 3 additions & 3 deletions src/main/kotlin/com/sainsburys/k2zpl/command/MediaMode.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import com.sainsburys.k2zpl.command.options.ZplYesNo
internal data class MediaMode(
val mediaMode: ZplMediaMode,
val prePeelSelect: ZplYesNo
) : ZplCommand {
) : ZplCommand(
parameters = listOf("m" to mediaMode, "p" to prePeelSelect)
) {
override val command: CharSequence = "^MM"
override val parameters: Map<CharSequence, Any?> =
addParameters("m" to mediaMode, "p" to prePeelSelect)
}

/**
Expand Down
5 changes: 3 additions & 2 deletions src/main/kotlin/com/sainsburys/k2zpl/command/PrintWidth.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ package com.sainsburys.k2zpl.command

import com.sainsburys.k2zpl.builder.ZplBuilder

internal data class PrintWidth(val width: Int) : ZplCommand {
internal data class PrintWidth(val width: Int) : ZplCommand(
parameters = listOf("w" to width)
) {
init {
require(width in 2 .. 32000) { "Width must be greater than 2. Value is also capped at width of the actual label." }
}

override val command: CharSequence = "^PW"
override val parameters: Map<CharSequence, Any?> = addParameters("w" to width)
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.sainsburys.k2zpl.command

import com.sainsburys.k2zpl.builder.ZplBuilder

internal data object StartFormat : ZplCommand {
internal data object StartFormat : ZplCommand() {
override val command: CharSequence = "^XA"
}

Expand Down
20 changes: 10 additions & 10 deletions src/main/kotlin/com/sainsburys/k2zpl/command/ZplCommand.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
package com.sainsburys.k2zpl.command

interface ZplCommand {
val command: CharSequence
val parameters: Map<CharSequence, Any?> get() = addParameters()
fun build(stringBuilder: StringBuilder) = stringBuilder.apply {
import java.util.Collections.unmodifiableMap

abstract class ZplCommand(
parameters: List<Pair<CharSequence, Any?>> = emptyList()
) {
val parameters: Map<CharSequence, Any?> = unmodifiableMap(parameters.toMap())
Copy link
Author

@jbduncan jbduncan Sep 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As alluded to in the OP, this use of unmodifiableMap is what I'm most ambivalent about, as on one hand it stops parameters being casted into a mutable map, but on the other hand it's not idiomatic in Kotlin and just .toMap() is what most people use by default.

What do you think?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @jbduncan,

I took at look at this and I think it made me realise we don't have to use a raw Map as the parameters here - we can just wrap it in another class where we have full control of the implementation and usage. Have a look at #21

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! I'm closing this PR in favour of yours then. :)


abstract val command: CharSequence

open fun build(stringBuilder: StringBuilder) = stringBuilder.apply {
append(command)
with(parameters.values.iterator()) {
if (hasNext()) {
Expand All @@ -24,9 +30,3 @@ interface ZplCommand {
private fun <T> Iterator<T>.nextNotNull(block: (T) -> Unit) {
next()?.let { block(it) }
}

/**
* A shortcut to adding parameters that helps to enforce use of [LinkedHashMap]
* so that entry order is preserved.
*/
internal fun <K, V> ZplCommand.addParameters(vararg pairs: Pair<K, V>) = linkedMapOf(*pairs)
30 changes: 16 additions & 14 deletions src/test/kotlin/com/sainsburys/k2zpl/command/ZplCommandTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,37 +27,39 @@ class ZplCommandTest : DescribeSpec({
}
})

class ZplCommandWithoutParameters : ZplCommand {
class ZplCommandWithoutParameters : ZplCommand() {
override val command = "^ZC"
}

class ZplCommandWithOneParameter : ZplCommand {
class ZplCommandWithOneParameter : ZplCommand(
parameters = listOf("param-one" to "value-one")
) {
override val command = "^ZCP"
override val parameters: Map<CharSequence, Any?> = addParameters(
"param-one" to "value-one"
)
}

class ZplCommandWithMultipleParameters : ZplCommand {
override val command = "^ZCPS"
override val parameters: Map<CharSequence, Any?> = addParameters(
class ZplCommandWithMultipleParameters : ZplCommand(
parameters = listOf(
"param-one" to "value-one",
"param-two" to "value-two"
)
) {
override val command = "^ZCPS"
}

class ZplCommandWitNullFirstParameter : ZplCommand {
override val command = "^ZCPN"
override val parameters: Map<CharSequence, Any?> = addParameters(
class ZplCommandWitNullFirstParameter : ZplCommand(
parameters = listOf(
"param-one" to null,
"param-two" to "value-two"
)
) {
override val command = "^ZCPN"
}

class ZplCommandWitNullSecondParameter : ZplCommand {
override val command = "^ZCPNS"
override val parameters: Map<CharSequence, Any?> = addParameters(
class ZplCommandWitNullSecondParameter : ZplCommand(
parameters = listOf(
"param-one" to "value-one",
"param-two" to null
)
) {
override val command = "^ZCPNS"
}