Fixes
Took 14 seconds
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
plugins {
|
plugins {
|
||||||
kotlin("multiplatform") version "1.5.21"
|
kotlin("multiplatform") version "1.5.30"
|
||||||
`maven-publish`
|
`maven-publish`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7,9 +7,9 @@ group = "nl.astraeus"
|
|||||||
version = "0.4.29-SNAPSHOT"
|
version = "0.4.29-SNAPSHOT"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
maven { setUrl("https://dl.bintray.com/kotlin/kotlin-eap") }
|
|
||||||
mavenLocal()
|
mavenLocal()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
maven { setUrl("https://dl.bintray.com/kotlin/kotlin-eap") }
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
|
|||||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,5 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|||||||
@@ -10,4 +10,4 @@ pluginManagement {
|
|||||||
|
|
||||||
rootProject.name = "kotlin-css-generator"
|
rootProject.name = "kotlin-css-generator"
|
||||||
|
|
||||||
enableFeaturePreview("GRADLE_METADATA")
|
//enableFeaturePreview("GRADLE_METADATA")
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ enum class MeasurementUoM {
|
|||||||
NONE,
|
NONE,
|
||||||
PX,
|
PX,
|
||||||
EM,
|
EM,
|
||||||
|
REL,
|
||||||
REM,
|
REM,
|
||||||
PC,
|
PC,
|
||||||
PRC,
|
PRC,
|
||||||
@@ -16,12 +17,23 @@ open class Measurement(
|
|||||||
val uom: MeasurementUoM = MeasurementUoM.NONE
|
val uom: MeasurementUoM = MeasurementUoM.NONE
|
||||||
) : CssProperty(value) {
|
) : CssProperty(value) {
|
||||||
|
|
||||||
|
override fun toString(): String = super.value
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val auto = Measurement("auto")
|
val auto = Measurement("auto")
|
||||||
val initial = Measurement("initial")
|
val initial = Measurement("initial")
|
||||||
val inherit = Measurement("inherit")
|
val inherit = Measurement("inherit")
|
||||||
val normal = Measurement("normal")
|
val normal = Measurement("normal")
|
||||||
|
|
||||||
|
fun fromString(value:String): Measurement = when {
|
||||||
|
value == "0" -> Measurement("0", MeasurementUoM.PX)
|
||||||
|
value.endsWith("px") -> Measurement(value.slice(0..(value.length-2)), MeasurementUoM.PX)
|
||||||
|
value.endsWith("rel") -> Measurement(value.slice(0..(value.length-3)), MeasurementUoM.REL)
|
||||||
|
else -> {
|
||||||
|
TODO("Unable to parse $value")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun px(nr: Int) = if (nr == 0) {
|
fun px(nr: Int) = if (nr == 0) {
|
||||||
Measurement(
|
Measurement(
|
||||||
"0",
|
"0",
|
||||||
|
|||||||
@@ -61,32 +61,140 @@ abstract class CssGenerator {
|
|||||||
return "$paddedName$builder;\n"
|
return "$paddedName$builder;\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun generatePropertyCss(indent: String): String {
|
fun generatePropertyCss(
|
||||||
|
indent: String,
|
||||||
|
sortProperties: Boolean
|
||||||
|
): String {
|
||||||
val builder = StringBuilder()
|
val builder = StringBuilder()
|
||||||
|
|
||||||
for ((name, prop) in props) {
|
if (sortProperties) {
|
||||||
builder.append(propertyCss(indent, name, prop))
|
for (name in props.keys.sorted()) {
|
||||||
|
val prop = props[name] ?: error("$name not found in properties after sorting!")
|
||||||
|
|
||||||
|
builder.append(propertyCss(indent, name, prop))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for ((name, prop) in props) {
|
||||||
|
builder.append(propertyCss(indent, name, prop))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.toString()
|
return builder.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun generateCss(
|
open fun generateCss(
|
||||||
|
indent: String = "",
|
||||||
|
minified: Boolean = false,
|
||||||
|
warnOnRedeclaration: Boolean = true,
|
||||||
|
allowCommaInSelector: Boolean = false,
|
||||||
|
combineEqualBlocks: Boolean = false,
|
||||||
|
sortProperties: Boolean = false
|
||||||
|
): String {
|
||||||
|
val blocks = generateCssBlocks(
|
||||||
|
indent = indent,
|
||||||
|
minified = minified,
|
||||||
|
warnOnRedeclaration = warnOnRedeclaration,
|
||||||
|
allowCommaInSelector = allowCommaInSelector,
|
||||||
|
sortProperties = sortProperties
|
||||||
|
)
|
||||||
|
|
||||||
|
val builder = StringBuilder()
|
||||||
|
|
||||||
|
fun StringBuilder.generateBlock(
|
||||||
|
indent: String,
|
||||||
|
selectors: List<String>,
|
||||||
|
block: CssBlock?
|
||||||
|
) {
|
||||||
|
if (selectors.isNotEmpty() && block != null) {
|
||||||
|
append(indent)
|
||||||
|
append(selectors.joinToString(",\n"))
|
||||||
|
append(" {\n")
|
||||||
|
append(block.content)
|
||||||
|
append(indent)
|
||||||
|
append("}\n\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!combineEqualBlocks) {
|
||||||
|
var first = true
|
||||||
|
val selectors = mutableListOf<String>()
|
||||||
|
var lastBlock: CssBlock? = null
|
||||||
|
|
||||||
|
for (block in blocks) {
|
||||||
|
if (first) {
|
||||||
|
first = false
|
||||||
|
selectors.add(block.selector)
|
||||||
|
lastBlock = block
|
||||||
|
} else {
|
||||||
|
lastBlock = if (lastBlock != null && lastBlock.content == block.content) {
|
||||||
|
selectors.add(block.selector)
|
||||||
|
block
|
||||||
|
} else {
|
||||||
|
builder.generateBlock(indent, selectors, lastBlock)
|
||||||
|
|
||||||
|
selectors.clear()
|
||||||
|
selectors.add(block.selector)
|
||||||
|
block
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.generateBlock(indent, selectors, lastBlock)
|
||||||
|
} else {
|
||||||
|
val blockHashes: MutableMap<Int, MutableList<CssBlock>> = mutableMapOf()
|
||||||
|
|
||||||
|
for (block in blocks) {
|
||||||
|
blockHashes.getOrPut(block.content.hashCode()) {
|
||||||
|
mutableListOf()
|
||||||
|
}.add(block)
|
||||||
|
}
|
||||||
|
val done = mutableSetOf<Int>()
|
||||||
|
|
||||||
|
for(block in blocks) {
|
||||||
|
val hashCode = block.content.hashCode()
|
||||||
|
|
||||||
|
if (!done.contains(hashCode)) {
|
||||||
|
blockHashes[hashCode]?.let {
|
||||||
|
val slctrs = it.map { blk ->
|
||||||
|
blk.selector
|
||||||
|
}
|
||||||
|
builder.generateBlock(indent, slctrs, block)
|
||||||
|
done.add(hashCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return if (minified) {
|
||||||
|
val stripped = StringBuilder()
|
||||||
|
val skip = arrayOf(' ', '\t', '\n', '\r')
|
||||||
|
for (char in builder) {
|
||||||
|
if (!skip.contains(char)) {
|
||||||
|
stripped.append(char)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stripped.toString()
|
||||||
|
} else {
|
||||||
|
builder.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun generateCssBlocks(
|
||||||
namespace: String = "",
|
namespace: String = "",
|
||||||
indent: String = "",
|
indent: String = "",
|
||||||
minified: Boolean = false,
|
minified: Boolean = false,
|
||||||
warnOnRedeclaration: Boolean = true,
|
warnOnRedeclaration: Boolean = true,
|
||||||
allowCommaInSelector: Boolean = false
|
allowCommaInSelector: Boolean = false,
|
||||||
): String {
|
sortProperties: Boolean = false
|
||||||
|
): List<CssBlock> {
|
||||||
val blocks = mutableListOf<CssBlock>()
|
val blocks = mutableListOf<CssBlock>()
|
||||||
val builder = StringBuilder()
|
|
||||||
|
|
||||||
for (name in definitions.keys) {
|
for (name in definitions.keys) {
|
||||||
val props = definitions[name]!!
|
val props = definitions[name]!!
|
||||||
val css = StringBuilder()
|
val css = StringBuilder()
|
||||||
|
|
||||||
if (warnOnRedeclaration && props.size > 1) {
|
if (warnOnRedeclaration && props.size > 1) {
|
||||||
css.append("/* style '$name' is defined ${props.size} times! */\n")
|
css.append(" $indent/* style '$name' is defined ${props.size} times! */\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
val finalStyle = Style()
|
val finalStyle = Style()
|
||||||
@@ -95,22 +203,24 @@ abstract class CssGenerator {
|
|||||||
prop(finalStyle)
|
prop(finalStyle)
|
||||||
}
|
}
|
||||||
|
|
||||||
css.append(finalStyle.generatePropertyCss(" $indent"))
|
css.append(finalStyle.generatePropertyCss(" $indent", sortProperties))
|
||||||
|
|
||||||
if (css.isNotBlank()) {
|
if (css.isNotBlank()) {
|
||||||
|
val builder = StringBuilder()
|
||||||
|
|
||||||
check (allowCommaInSelector || !name.contains(',')) {
|
check (allowCommaInSelector || !name.contains(',')) {
|
||||||
"Comma is not allowed in selector (option is set in generateCss call)"
|
"Comma is not allowed in selector (option is set in generateCss call)"
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.append("\n$namespace$name".trim())
|
//builder.append("\n$namespace$name".trim())
|
||||||
|
|
||||||
//builder.append(" $indent")
|
//builder.append(" $indent")
|
||||||
builder.append(" {\n")
|
//builder.append(" {\n")
|
||||||
|
|
||||||
finalStyle.fontFace?.let { ff ->
|
finalStyle.fontFace?.let { ff ->
|
||||||
builder.append(" $indent")
|
builder.append(" $indent")
|
||||||
builder.append("@font-face {\n")
|
builder.append("@font-face {\n")
|
||||||
builder.append(ff.generatePropertyCss(" $indent"))
|
builder.append(ff.generatePropertyCss(" $indent", sortProperties))
|
||||||
builder.append(" $indent")
|
builder.append(" $indent")
|
||||||
builder.append("}\n")
|
builder.append("}\n")
|
||||||
}
|
}
|
||||||
@@ -134,7 +244,7 @@ abstract class CssGenerator {
|
|||||||
|
|
||||||
style(finalStyle)
|
style(finalStyle)
|
||||||
|
|
||||||
builder.append(finalStyle.generatePropertyCss(" $indent"))
|
builder.append(finalStyle.generatePropertyCss(" $indent", sortProperties))
|
||||||
|
|
||||||
builder.append(" $indent")
|
builder.append(" $indent")
|
||||||
builder.append("}\n")
|
builder.append("}\n")
|
||||||
@@ -147,13 +257,21 @@ abstract class CssGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
builder.append(css)
|
builder.append(css)
|
||||||
builder.append("}\n\n")
|
//builder.append("}\n\n")
|
||||||
|
|
||||||
|
blocks.add(CssBlock(
|
||||||
|
"$namespace$name".trim(),
|
||||||
|
builder.toString()
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.append(finalStyle.generateCss(
|
blocks.addAll(finalStyle.generateCssBlocks(
|
||||||
"$namespace$name".trim(),
|
"$namespace$name".trim(),
|
||||||
indent,
|
indent,
|
||||||
allowCommaInSelector = allowCommaInSelector
|
minified = minified,
|
||||||
|
allowCommaInSelector = allowCommaInSelector,
|
||||||
|
warnOnRedeclaration = warnOnRedeclaration,
|
||||||
|
sortProperties = sortProperties
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,24 +280,22 @@ abstract class CssGenerator {
|
|||||||
mq.keys.sorted().forEach { mediaName ->
|
mq.keys.sorted().forEach { mediaName ->
|
||||||
val css = mq[mediaName]
|
val css = mq[mediaName]
|
||||||
|
|
||||||
builder.append(indent)
|
|
||||||
builder.append("@media ")
|
|
||||||
builder.append(mediaName)
|
|
||||||
builder.append(" {\n")
|
|
||||||
css?.let { css ->
|
css?.let { css ->
|
||||||
val mediaStyle = ConditionalStyle()
|
val mediaStyle = ConditionalStyle()
|
||||||
|
|
||||||
css(mediaStyle)
|
css(mediaStyle)
|
||||||
|
|
||||||
builder.append(mediaStyle.generateCss(
|
blocks.add(CssBlock(
|
||||||
"",
|
"$indent@media $mediaName".trim(),
|
||||||
" $indent",
|
mediaStyle.generateCss(
|
||||||
allowCommaInSelector = allowCommaInSelector
|
" $indent",
|
||||||
|
minified = minified,
|
||||||
|
allowCommaInSelector = allowCommaInSelector,
|
||||||
|
warnOnRedeclaration = warnOnRedeclaration,
|
||||||
|
sortProperties = sortProperties
|
||||||
|
)
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.append(indent)
|
|
||||||
builder.append("}\n")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,40 +303,27 @@ abstract class CssGenerator {
|
|||||||
mq.keys.sorted().forEach { mediaName ->
|
mq.keys.sorted().forEach { mediaName ->
|
||||||
val css = mq[mediaName]
|
val css = mq[mediaName]
|
||||||
|
|
||||||
builder.append(indent)
|
|
||||||
builder.append("@supports ")
|
|
||||||
builder.append(mediaName)
|
|
||||||
builder.append(" {\n")
|
|
||||||
css?.let { css ->
|
css?.let { css ->
|
||||||
val mediaStyle = ConditionalStyle()
|
val mediaStyle = ConditionalStyle()
|
||||||
|
|
||||||
css(mediaStyle)
|
css(mediaStyle)
|
||||||
|
|
||||||
builder.append(mediaStyle.generateCss(
|
blocks.add(CssBlock(
|
||||||
"",
|
"$indent@supports $mediaName".trim(),
|
||||||
" $indent",
|
mediaStyle.generateCss(
|
||||||
allowCommaInSelector = allowCommaInSelector
|
" $indent",
|
||||||
|
minified = minified,
|
||||||
|
allowCommaInSelector = allowCommaInSelector,
|
||||||
|
warnOnRedeclaration = warnOnRedeclaration,
|
||||||
|
sortProperties = sortProperties
|
||||||
|
)
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.append(indent)
|
|
||||||
builder.append("}\n")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return if (minified) {
|
return blocks
|
||||||
val stripped = StringBuilder()
|
|
||||||
val skip = arrayOf(' ', '\t', '\n', '\r')
|
|
||||||
for (char in builder) {
|
|
||||||
if (!skip.contains(char)) {
|
|
||||||
stripped.append(char)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stripped.toString()
|
|
||||||
} else {
|
|
||||||
builder.toString()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -382,7 +485,19 @@ open class Style : CssGenerator() {
|
|||||||
addStyle(":hover", style)
|
addStyle(":hover", style)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun pseudo(selector: DescriptionProvider, style: Css) {
|
fun firstChild(style: Css) {
|
||||||
|
addStyle(":first-child", style)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun lastChild(style: Css) {
|
||||||
|
addStyle(":last-child", style)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun pseudoElement(selector: DescriptionProvider, style: Css) {
|
||||||
|
addStyle(":${selector.description()}", style)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun pseudoChild(selector: DescriptionProvider, style: Css) {
|
||||||
addStyle("::${selector.description()}", style)
|
addStyle("::${selector.description()}", style)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -486,6 +601,10 @@ open class Style : CssGenerator() {
|
|||||||
props["background-image"] = prp(image)
|
props["background-image"] = prp(image)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun backgroundImage(value: String) {
|
||||||
|
props["background-image"] = prp(value)
|
||||||
|
}
|
||||||
|
|
||||||
fun backgroundOrigin(origin: ClipOrigin) {
|
fun backgroundOrigin(origin: ClipOrigin) {
|
||||||
props["background-origin"] = prp(origin)
|
props["background-origin"] = prp(origin)
|
||||||
}
|
}
|
||||||
@@ -506,6 +625,14 @@ open class Style : CssGenerator() {
|
|||||||
props["border"] = prp(border)
|
props["border"] = prp(border)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun border(
|
||||||
|
width: Measurement,
|
||||||
|
style: BorderStyle,
|
||||||
|
color: Color
|
||||||
|
) {
|
||||||
|
props["border"] = prp(width, style, color)
|
||||||
|
}
|
||||||
|
|
||||||
fun borderBottom(borderBottom: String) {
|
fun borderBottom(borderBottom: String) {
|
||||||
props["border-bottom"] = prp(borderBottom)
|
props["border-bottom"] = prp(borderBottom)
|
||||||
}
|
}
|
||||||
@@ -514,10 +641,18 @@ open class Style : CssGenerator() {
|
|||||||
props["border-bottom-color"] = prp(color)
|
props["border-bottom-color"] = prp(color)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun borderBottomLeftRadius(vararg radius: Measurement) {
|
||||||
|
props["border-bottom-left-radius"] = prp(*radius)
|
||||||
|
}
|
||||||
|
|
||||||
fun borderBottomLeftRadius(vararg radius: BorderRadius) {
|
fun borderBottomLeftRadius(vararg radius: BorderRadius) {
|
||||||
props["border-bottom-left-radius"] = prp(*radius)
|
props["border-bottom-left-radius"] = prp(*radius)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun borderBottomRightRadius(vararg radius: Measurement) {
|
||||||
|
props["border-bottom-right-radius"] = prp(*radius)
|
||||||
|
}
|
||||||
|
|
||||||
fun borderBottomRightRadius(vararg radius: BorderRadius) {
|
fun borderBottomRightRadius(vararg radius: BorderRadius) {
|
||||||
props["border-bottom-right-radius"] = prp(*radius)
|
props["border-bottom-right-radius"] = prp(*radius)
|
||||||
}
|
}
|
||||||
@@ -656,10 +791,18 @@ open class Style : CssGenerator() {
|
|||||||
props["border-top-color"] = prp(color)
|
props["border-top-color"] = prp(color)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun borderTopLeftRadius(radius: Measurement) {
|
||||||
|
props["border-top-left-radius"] = prp(radius)
|
||||||
|
}
|
||||||
|
|
||||||
fun borderTopLeftRadius(radius: BorderRadius) {
|
fun borderTopLeftRadius(radius: BorderRadius) {
|
||||||
props["border-top-left-radius"] = prp(radius)
|
props["border-top-left-radius"] = prp(radius)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun borderTopRightRadius(radius: Measurement) {
|
||||||
|
props["border-top-right-radius"] = prp(radius)
|
||||||
|
}
|
||||||
|
|
||||||
fun borderTopRightRadius(radius: BorderRadius) {
|
fun borderTopRightRadius(radius: BorderRadius) {
|
||||||
props["border-top-right-radius"] = prp(radius)
|
props["border-top-right-radius"] = prp(radius)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package nl.astraeus.css
|
package nl.astraeus.css
|
||||||
|
|
||||||
|
import nl.astraeus.css.properties.BoxSizing
|
||||||
import nl.astraeus.css.properties.Color
|
import nl.astraeus.css.properties.Color
|
||||||
import nl.astraeus.css.properties.Count
|
import nl.astraeus.css.properties.Count
|
||||||
import nl.astraeus.css.properties.Display
|
import nl.astraeus.css.properties.Display
|
||||||
@@ -24,6 +25,32 @@ class TestCssBuilder {
|
|||||||
fun testBuilder() {
|
fun testBuilder() {
|
||||||
val css = style {
|
val css = style {
|
||||||
|
|
||||||
|
select("*", "*::before", "*::after") {
|
||||||
|
boxSizing(BoxSizing.borderBox)
|
||||||
|
}
|
||||||
|
|
||||||
|
select("html") {
|
||||||
|
transition("background-color 1s ease")
|
||||||
|
|
||||||
|
margin(0.px)
|
||||||
|
padding(0.px)
|
||||||
|
|
||||||
|
focus {
|
||||||
|
backgroundColor(Color.blue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
select("body") {
|
||||||
|
margin(0.px)
|
||||||
|
|
||||||
|
padding(0.px)
|
||||||
|
|
||||||
|
focus {
|
||||||
|
backgroundColor(Color.blue)
|
||||||
|
}
|
||||||
|
transition("background-color 1s ease")
|
||||||
|
}
|
||||||
|
|
||||||
select(".test") {
|
select(".test") {
|
||||||
top(10.px)
|
top(10.px)
|
||||||
left(4.em)
|
left(4.em)
|
||||||
@@ -77,7 +104,7 @@ class TestCssBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
println(css.generateCss())
|
println(css.generateCss(combineEqualBlocks = true, sortProperties = true))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user