Skip to content

Commit 8082d0d

Browse files
committed
Migrated to UI DSL v2. Fixed symbol validation logic.
1 parent 5444775 commit 8082d0d

File tree

3 files changed

+174
-112
lines changed

3 files changed

+174
-112
lines changed

plugin/src/main/kotlin/com/mitteloupe/cag/cleanarchitecturegenerator/CreateUseCaseDialog.kt

Lines changed: 46 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,15 @@ import com.intellij.ui.components.JBTextField
1414
import com.intellij.ui.dsl.builder.Align
1515
import com.intellij.ui.dsl.builder.bindText
1616
import com.intellij.ui.dsl.builder.panel
17+
import com.intellij.ui.dsl.builder.whenItemChangedFromUi
1718
import com.intellij.util.ui.UIUtil
1819
import com.mitteloupe.cag.cleanarchitecturegenerator.form.OnChangeDocumentListener
1920
import com.mitteloupe.cag.cleanarchitecturegenerator.form.PredicateDocumentFilter
2021
import com.mitteloupe.cag.cleanarchitecturegenerator.validation.SymbolValidator
21-
import java.awt.event.FocusAdapter
22-
import java.awt.event.FocusEvent
22+
import java.awt.EventQueue.invokeLater
2323
import java.io.File
2424
import javax.swing.DefaultComboBoxModel
25-
import javax.swing.JComponent
26-
import javax.swing.SwingUtilities
25+
import javax.swing.JTextField
2726
import javax.swing.text.AbstractDocument
2827

2928
private const val USE_CASE_SUFFIX = "UseCase"
@@ -36,26 +35,28 @@ class CreateUseCaseDialog(
3635
) : DialogWrapper(project) {
3736
private val useCaseNameTextField = JBTextField()
3837
private val directoryField = TextFieldWithBrowseButton()
39-
private val inputDataTypeComboBox = ComboBox<String>()
38+
private lateinit var inputDataTypeComboBox: ComboBox<String>
4039
private val inputWarningLabel = JBLabel()
41-
private val outputDataTypeComboBox = ComboBox<String>()
40+
private lateinit var outputDataTypeComboBox: ComboBox<String>
4241
private val outputWarningLabel = JBLabel()
4342
private val modelClassFinder = ModelClassFinder()
4443
private val symbolValidator = SymbolValidator()
45-
46-
private var documentListenersSetup = false
44+
private val inputDataTypeModel = DefaultComboBoxModel<String>()
45+
private val outputDataTypeModel = DefaultComboBoxModel<String>()
4746

4847
val useCaseNameWithSuffix: String
4948
get() = useCaseName.removeSuffix(USE_CASE_SUFFIX) + USE_CASE_SUFFIX
5049

5150
private val useCaseName: String
5251
get() = useCaseNameTextField.text.trim()
5352

53+
private var _inputDataType: String? = null
5454
val inputDataType: String?
55-
get() = (inputDataTypeComboBox.selectedItem as? String)?.trim()?.takeIf { it.isNotEmpty() }
55+
get() = _inputDataType
5656

57+
private var _outputDataType: String? = null
5758
val outputDataType: String?
58-
get() = (outputDataTypeComboBox.selectedItem as? String)?.trim()?.takeIf { it.isNotEmpty() }
59+
get() = _outputDataType
5960

6061
val destinationDirectory: File?
6162
get() = directoryField.text.trim().takeIf { it.isNotEmpty() }?.let { File(it) }
@@ -86,17 +87,17 @@ class CreateUseCaseDialog(
8687
setupWarningLabels()
8788
}
8889

89-
override fun getPreferredFocusedComponent(): JComponent = useCaseNameTextField
90+
override fun getPreferredFocusedComponent() = useCaseNameTextField
9091

9192
override fun show() {
9293
super.show()
9394

94-
SwingUtilities.invokeLater {
95+
invokeLater {
9596
validateFieldOnChange()
9697
}
9798
}
9899

99-
override fun createCenterPanel(): JComponent =
100+
override fun createCenterPanel() =
100101
panel {
101102
row(CleanArchitectureGeneratorBundle.message("dialog.usecase.name.label")) {
102103
textField()
@@ -108,11 +109,34 @@ class CreateUseCaseDialog(
108109
)
109110
}
110111
row(CleanArchitectureGeneratorBundle.message("dialog.usecase.input.type.label")) {
111-
cell(inputDataTypeComboBox)
112+
@Suppress("UnstableApiUsage")
113+
comboBox(inputDataTypeModel)
114+
.whenItemChangedFromUi { _inputDataType = it }
115+
.applyToComponent {
116+
isEditable = true
117+
(editor.editorComponent as JTextField).validateOnChange {
118+
_inputDataType = text
119+
}
120+
selectedItem = DEFAULT_DATA_TYPE
121+
inputDataTypeComboBox = this
122+
}
112123
cell(inputWarningLabel)
113124
}
114125
row(CleanArchitectureGeneratorBundle.message("dialog.usecase.output.type.label")) {
115-
cell(outputDataTypeComboBox)
126+
@Suppress("UnstableApiUsage")
127+
comboBox(outputDataTypeModel)
128+
.whenItemChangedFromUi { _outputDataType = it }
129+
.applyToComponent {
130+
isEditable = true
131+
(editor.editorComponent as JTextField).validateOnChange {
132+
_inputDataType = text
133+
}
134+
addActionListener {
135+
validateFieldOnChange()
136+
}
137+
selectedItem = DEFAULT_DATA_TYPE
138+
outputDataTypeComboBox = this
139+
}
116140
cell(outputWarningLabel)
117141
}
118142
row(CleanArchitectureGeneratorBundle.message("dialog.usecase.directory.field.label")) {
@@ -137,22 +161,6 @@ class CreateUseCaseDialog(
137161
outputWarningLabel.clearWarning()
138162
}
139163

140-
private fun setupDocumentListenersIfNeeded() {
141-
if (documentListenersSetup) {
142-
return
143-
}
144-
145-
val inputEditor = inputDataTypeComboBox.editor.editorComponent as? JBTextField
146-
val outputEditor = outputDataTypeComboBox.editor.editorComponent as? JBTextField
147-
148-
if (inputEditor != null && outputEditor != null) {
149-
inputEditor.validateOnChange()
150-
outputEditor.validateOnChange()
151-
152-
documentListenersSetup = true
153-
}
154-
}
155-
156164
private fun validateFieldOnChange() {
157165
inputDataType.validateDataType(inputWarningLabel)
158166
outputDataType.validateDataType(outputWarningLabel)
@@ -228,33 +236,17 @@ class CreateUseCaseDialog(
228236
val modelClasses = modelClassFinder.findModelClasses(destinationDirectory)
229237
val allOptions = ModelClassFinder.PRIMITIVE_TYPES + modelClasses
230238

231-
inputDataTypeComboBox.enableAndPopulateComboBox(allOptions)
232-
outputDataTypeComboBox.enableAndPopulateComboBox(allOptions)
233-
}
234-
}
235-
236-
private fun <T> ComboBox<T>.enableAndPopulateComboBox(options: List<T>) {
237-
isEditable = true
238-
addActionListener {
239-
validateFieldOnChange()
239+
inputDataTypeModel.addAll(allOptions)
240+
outputDataTypeModel.addAll(allOptions)
240241
}
241-
model = DefaultComboBoxModel<T>().apply { addAll(options) }
242-
selectedItem = DEFAULT_DATA_TYPE
243242
}
244243

245-
private fun JBTextField.validateOnChange() {
246-
document.addDocumentListener(OnChangeDocumentListener { validateFieldOnChange() })
247-
addFocusListener(
248-
object : FocusAdapter() {
249-
override fun focusGained(event: FocusEvent) {
250-
setupDocumentListenersIfNeeded()
251-
}
252-
253-
override fun focusLost(event: FocusEvent) {
254-
validateFieldOnChange()
255-
}
244+
private fun JTextField.validateOnChange(onChange: JTextField.() -> Unit) {
245+
document.addDocumentListener(
246+
OnChangeDocumentListener {
247+
onChange()
248+
validateFieldOnChange()
256249
}
257250
)
258-
addActionListener { validateFieldOnChange() }
259251
}
260252
}

plugin/src/main/kotlin/com/mitteloupe/cag/cleanarchitecturegenerator/validation/SymbolValidator.kt

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ class SymbolValidator(
1010
private val fileSystemWrapper: FileSystemWrapper = IntelliJFileSystemWrapper()
1111
) {
1212
fun isValidSymbolSyntax(type: String): Boolean {
13-
if (type.isEmpty()) return false
13+
if (type.isBlank()) {
14+
return false
15+
}
1416
val normalizedType = type.trim()
1517
val baseType = normalizedType.substringBefore('<')
1618
return isValidKotlinIdentifier(baseType) &&
@@ -22,7 +24,7 @@ class SymbolValidator(
2224
contextDirectory: File
2325
): Boolean {
2426
if (!isValidSymbolSyntax(type)) {
25-
return true
27+
return false
2628
}
2729

2830
val baseType = type.trim().substringBefore('<')
@@ -35,13 +37,30 @@ class SymbolValidator(
3537
return true
3638
}
3739

38-
return findTypeInModule(baseType, contextDirectory)
40+
val entityName =
41+
if (baseType.contains('.')) {
42+
baseType.substringAfterLast('.')
43+
} else {
44+
baseType
45+
}
46+
47+
return findTypeInModule(entityName, contextDirectory)
3948
}
4049

4150
private fun isValidKotlinIdentifier(identifier: String): Boolean {
42-
if (identifier.isEmpty()) return false
43-
if (!identifier.first().isLetter() && identifier.first() != '_') return false
44-
return identifier.all { it.isLetterOrDigit() || it == '_' }
51+
val parts = identifier.split('.')
52+
parts.forEach { part ->
53+
if (part.isEmpty()) {
54+
return false
55+
}
56+
if (!part.first().isLetter() && part.first() != '_') {
57+
return false
58+
}
59+
if (!part.all { it.isLetterOrDigit() || it == '_' }) {
60+
return false
61+
}
62+
}
63+
return true
4564
}
4665

4766
private fun hasValidGenericSyntax(type: String): Boolean {
@@ -114,15 +133,13 @@ class SymbolValidator(
114133
return null
115134
}
116135

117-
private fun findSourceDirectory(moduleRoot: File): File? {
118-
val srcMain = File(moduleRoot, "src/main/kotlin")
119-
if (srcMain.exists()) return srcMain
120-
121-
val src = File(moduleRoot, "src")
122-
if (src.exists()) return src
123-
124-
return null
125-
}
136+
private fun findSourceDirectory(moduleRoot: File): File? =
137+
listOf("src/main/kotlin", "src/main/java", "src")
138+
.asSequence()
139+
.map { path -> File(moduleRoot, path) }
140+
.firstOrNull { file ->
141+
file.exists() && file.isDirectory
142+
}
126143

127144
private fun searchForTypeInDirectory(
128145
typeName: String,
@@ -155,7 +172,7 @@ class SymbolValidator(
155172
): Boolean {
156173
val patterns =
157174
listOf(
158-
"""(?:class|interface|object|enum class)\s+$typeName(?:\s|<|$)""".toRegex(),
175+
"""(?:class|interface|object|enum class)\s+$typeName(?:\s|<|\(|$)""".toRegex(),
159176
"""typealias\s+$typeName(?:\s|<|=)""".toRegex()
160177
)
161178

0 commit comments

Comments
 (0)