From fa05584827210aaaa9ca7cc1c425096252b64c8d Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Sat, 27 Aug 2016 17:25:15 -0700 Subject: [PATCH] Split validation and import. Added validation for unused fields. --- .../tesremoteprogrammer/MainActivity.kt | 147 ++++++++++-------- .../android/tesremoteprogrammer/util/Dtmf.kt | 4 +- app/src/main/res/values/strings.xml | 1 + 3 files changed, 85 insertions(+), 67 deletions(-) diff --git a/app/src/main/java/net/thauvin/erik/android/tesremoteprogrammer/MainActivity.kt b/app/src/main/java/net/thauvin/erik/android/tesremoteprogrammer/MainActivity.kt index 6ae5db7..6c8ed2a 100644 --- a/app/src/main/java/net/thauvin/erik/android/tesremoteprogrammer/MainActivity.kt +++ b/app/src/main/java/net/thauvin/erik/android/tesremoteprogrammer/MainActivity.kt @@ -72,10 +72,42 @@ class MainActivity : AppCompatActivity(), AnkoLogger { val PAUSE = ',' } + @NeedsPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) + fun importConfig(intent: Intent) { + val errors = StringBuilder() + + val tmp: Config? = try { + Gson().fromJson(InputStreamReader(contentResolver.openInputStream(intent.data)), + Config::class.java) + } catch (jse: JsonSyntaxException) { + val cause = jse.cause + if (cause != null) { + errors.append(cause.message) + } else { + errors.append(jse.message) + } + null + } + + if (tmp != null && validateConfig(tmp, errors)) { + config = tmp + saveConfig() + recreate() + } + + if (errors.length > 0) { + alert { + title(R.string.alert_config_error) + message(Html.fromHtml("$errors")) + cancelButton { } + }.show() + } + } + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { if (requestCode == read_request_code && resultCode == Activity.RESULT_OK) { if (data != null) { - MainActivityPermissionsDispatcher.validateConfigWithCheck(this, data) + MainActivityPermissionsDispatcher.importConfigWithCheck(this, data) } } } @@ -271,6 +303,12 @@ class MainActivity : AppCompatActivity(), AnkoLogger { defaultConfigs.forEach { config = Gson().fromJson(InputStreamReader(resources.openRawResource(it)), Config::class.java) + +// val errors = StringBuilder() +// if (!validateConfig(config, errors)) { +// info("${config.params.name}: $errors") +// } + confs.configs.put(config.params.name, config) } @@ -297,91 +335,70 @@ class MainActivity : AppCompatActivity(), AnkoLogger { } } - @NeedsPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) - fun validateConfig(intent: Intent) { - val errors = StringBuilder() + fun validateConfig(config: Config, errors: StringBuilder): Boolean { + val len = errors.length - val tmp: Config? = try { - Gson().fromJson(InputStreamReader(contentResolver.openInputStream(intent.data)), - Config::class.java) - } catch (jse: JsonSyntaxException) { - val cause = jse.cause - if (cause != null) { - errors.append(cause.message) - } else { - errors.append(jse.message) + with(config) { + if (params.name.isBlank()) { + errors.append(getString(R.string.validate_missing_param, "name")) } - null - } - if (tmp != null) { - with(tmp) { - if (params.name.isBlank()) { - errors.append(getString(R.string.validate_missing_param, "name")) + if (params.size < 1) { + errors.append(getString(R.string.validate_invalid_param, "size")) + } + + if (params.star.isBlank()) { + errors.append(getString(R.string.validate_missing_param, "star")) + } + + if (opts.size == 0) { + errors.append(getString(R.string.validate_missing_opts)) + } + + opts.forEachIndexed { i, option -> + if (option.fields.size == 0) { + errors.append(getString(R.string.validate_missing_fields, i + 1)) } - if (params.size < 1) { - errors.append(getString(R.string.validate_invalid_param, "size")) + if (option.nosteps && option.nodial) { + errors.append(getString(R.string.validate_invalid_option, i + 1, "nodial/nosteps")) } - if (params.star.isBlank()) { - errors.append(getString(R.string.validate_missing_param, "star")) + if (option.dtmf.isBlank()) { + errors.append(getString(R.string.validate_invalid_dtmf, i + 1, "''")) } - if (opts.size == 0) { - errors.append(getString(R.string.validate_missing_opts)) - } - - opts.forEachIndexed { i, option -> - if (option.fields.size == 0) { - errors.append(getString(R.string.validate_missing_fields, i + 1)) + option.fields.forEachIndexed { j, field -> + if (field.size <= 0) { + errors.append(getString(R.string.validate_invalid_attr, i + 1, j + 1, "size")) } - if (option.nosteps && option.nodial) { - errors.append(getString(R.string.validate_invalid_option, i + 1, "nodial/nosteps")) - } - - option.fields.forEachIndexed { j, field -> - if (field.size <= 0) { - errors.append(getString(R.string.validate_invalid_attr, i + 1, j + 1, "size")) - } - - if (!field.alpha) { - if (field.min >= 0 || field.max >= 0) { - if (field.max < 1) { - errors.append(getString(R.string.validate_invalid_attr, i + 1, j + 1, "max")) - } else if (field.min < 0) { - errors.append(getString(R.string.validate_invalid_attr, i + 1, j + 1, "min")) - } else if (field.min > field.max) { - errors.append(getString(R.string.validate_invalid_attr, i + 1, j + 1, "max/min")) - } + if (!field.alpha) { + if (field.min >= 0 || field.max >= 0) { + if (field.max < 1) { + errors.append(getString(R.string.validate_invalid_attr, i + 1, j + 1, "max")) + } else if (field.min < 0) { + errors.append(getString(R.string.validate_invalid_attr, i + 1, j + 1, "min")) + } else if (field.min > field.max) { + errors.append(getString(R.string.validate_invalid_attr, i + 1, j + 1, "max/min")) } } } - val blank = "\\0" - val dtmf = Dtmf.mock(option, blank) - if (!Dtmf.validate(dtmf, "${MainActivity.PAUSE}${params.star}${params.hash}$blank")) { - errors.append(getString(R.string.validate_invalid_dtmf, i + 1, dtmf.replace(blank, "✓"))) - + if (!option.dtmf.contains(Dtmf.DTMF_FIELD.format(j + 1))) { + errors.append(getString(R.string.validate_unused_field, i + 1, j + 1)) } } - } - if (errors.length == 0) { - config = tmp - saveConfig() - recreate() + val blank = "\\0" + val dtmf = Dtmf.mock(option, blank) + if (!Dtmf.validate(dtmf, "${MainActivity.Companion.PAUSE}${params.star}${params.hash}$blank")) { + errors.append(getString(R.string.validate_invalid_dtmf, i + 1, dtmf.replace(blank, "✓"))) + } } } - if (errors.length > 0) { - alert { - title(R.string.alert_config_error) - message(Html.fromHtml("$errors")) - cancelButton { } - }.show() - } + return errors.length == len } fun validateFields(fields: ArrayList, size: Int): Boolean { diff --git a/app/src/main/java/net/thauvin/erik/android/tesremoteprogrammer/util/Dtmf.kt b/app/src/main/java/net/thauvin/erik/android/tesremoteprogrammer/util/Dtmf.kt index f09c41e..4692b93 100644 --- a/app/src/main/java/net/thauvin/erik/android/tesremoteprogrammer/util/Dtmf.kt +++ b/app/src/main/java/net/thauvin/erik/android/tesremoteprogrammer/util/Dtmf.kt @@ -24,8 +24,8 @@ import java.util.* class Dtmf { companion object { - private val DTMF_MASTER = "[MASTER]" - private val DTMF_FIELD = "[FIELD:%1\$d]" + val DTMF_MASTER = "[MASTER]" + val DTMF_FIELD = "[FIELD:%1\$d]" private fun alphaToDigits(text: String, star: String): String { val result = StringBuffer() diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f543305..1fd13b5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -21,4 +21,5 @@ <p><b>opts[%1$d]</b>: <font color=\"red\">fields</font> missing <p><p><b>params</b>: <font color=\"red\">%1$s</font> missing</p> <font color=\"red\">opts</font> missing. + <b>opts[%1$d]</b>, <font color=\"red\">fields[%2$d]</font>: unused