turns-00036.parquet:33753
20dcb41b28a53b7209bd9119
turn 1/1o1-mini-2024-09-12EnglishBrazil1138 words
degenerate_repetitionAbsentFinal dense release
USER
package ui
import data.FileUtilities
import data.Message
import ui.interfaces.UserNotifier
import ui.interfaces.VideoFileSelector
import java.io.File
import java.nio.file.Paths
import java.util.ResourceBundle
import java.util.logging.Logger
import javax.swing.JFileChooser
import kotlin.system.exitProcess
class JFileChooserVideoFileSelector(
private val notifier: UserNotifier = JOptionPaneUserNotifier(),
private val logger: Logger = Logger.getLogger(JFileChooserVideoFileSelector::class.java.name),
private val messages: ResourceBundle = Message().messages,
private var lastDirectory: File = File(Paths.get(System.getProperty("user.home"), "Downloads").toString()),
private val fileUtilities: FileUtilities = FileUtilities()
) : VideoFileSelector {
override fun selectVideoFiles(many: Boolean): List<File> {
val fileChooser = createFileChooser(many)
val userSelection = fileChooser.showOpenDialog(null)
return if (userSelection == JFileChooser.APPROVE_OPTION) {
if (fileChooser.selectedFiles.isNotEmpty()) {
val filteredSelectedFiles = fileChooser.selectedFiles.filter { !fileUtilities.areItEmpty(it) }
if (filteredSelectedFiles.isNotEmpty()) {
handleFileSelection(filteredSelectedFiles)
} else {
notifier.showWarning(
"Todos Arquivos selecionados de ${fileChooser.selectedFiles.first().parentFile} estão vazios!\nSelecione outros arquivos"
)
selectVideoFiles(many)
}
} else if (fileChooser.selectedFile != null) {
if (fileUtilities.areItEmpty(fileChooser.selectedFile)) {
notifier.showWarning(
"Arquivo ${fileChooser.selectedFile.name} está vazio!\nSelecione outro arquivo"
)
selectVideoFiles(many)
} else {
handleFileSelection(listOf(fileChooser.selectedFile))
}
} else {
throw RuntimeException("Can't define the file(s) in fileChooser!")
}
} else {
exitProcess(0)
}
}
private fun createFileChooser(many: Boolean): JFileChooser {
return JFileChooser().apply {
currentDirectory = lastDirectory
fileSelectionMode = JFileChooser.FILES_ONLY
if (many) {
isMultiSelectionEnabled = true
}
dialogTitle = messages.getString("dialogTitle")
}
}
private fun handleFileSelection(files: List<File>): List<File> {
if (files.size < 2) {
files.forEach { file ->
logger.info("Selected file: ${file.absolutePath} is ${file.length()} in size")
if (file.length() <= 2_000_000_000 && file.extension == "mp4") {
if (notifier.smallFile() == 0) {
return selectVideoFiles()
} else {
logger.info("User choose not to proceed.")
exitProcess(0)
}
}
}
}
lastDirectory = files.first().parentFile
return files
}
}
melhore esse código com boas práticas com SOLID E CLEANCODEASSISTANT
Para aprimorar o código existente aplicando os princípios **SOLID** e as boas práticas de **Clean Code**, podemos realizar várias refatorações. Abaixo, apresento uma versão melhorada do `JFileChooserVideoFileSelector`, seguida de uma explicação detalhada das mudanças implementadas.
```kotlin
package ui
import data.FileUtilities
import data.Message
import ui.interfaces.UserNotifier
import ui.interfaces.VideoFileSelector
import java.io.File
import java.nio.file.Paths
import java.util.ResourceBundle
import java.util.logging.Logger
import javax.swing.JFileChooser
class JFileChooserVideoFileSelector(
private val notifier: UserNotifier,
private val logger: Logger,
private val messages: ResourceBundle,
private var lastDirectory: File,
private val fileUtilities: FileUtilities
) : VideoFileSelector {
companion object {
private const val DEFAULT_MAX_FILE_SIZE = 2_000_000_000L
private val DEFAULT_LAST_DIRECTORY = Paths.get(System.getProperty("user.home"), "Downloads").toFile()
}
constructor() : this(
notifier = JOptionPaneUserNotifier(),
logger = Logger.getLogger(JFileChooserVideoFileSelector::class.java.name),
messages = Message().messages,
lastDirectory = DEFAULT_LAST_DIRECTORY,
fileUtilities = FileUtilities()
)
override fun selectVideoFiles(multipleSelection: Boolean): List<File> {
while (true) {
val fileChooser = createFileChooser(multipleSelection)
val userSelection = fileChooser.showOpenDialog(null)
if (userSelection != JFileChooser.APPROVE_OPTION) {
throw UserCancelledException("User cancelled the file selection process.")
}
val selectedFiles = if (multipleSelection) {
fileChooser.selectedFiles.toList()
} else {
listOfNotNull(fileChooser.selectedFile)
}
val nonEmptyFiles = selectedFiles.filter { !fileUtilities.isEmpty(it) }
if (nonEmptyFiles.isEmpty()) {
showEmptyFilesWarning(selectedFiles.firstOrNull()?.parentFile)
continue
}
val invalidFiles = nonEmptyFiles.filter { it.length() <= DEFAULT_MAX_FILE_SIZE && it.extension.lowercase() == "mp4" }
if (invalidFiles.isNotEmpty()) {
val shouldRetry = handleInvalidFiles(invalidFiles)
if (!shouldRetry) {
throw InvalidFileException("User chose not to proceed with invalid files.")
}
continue
}
lastDirectory = nonEmptyFiles.first().parentFile
return nonEmptyFiles
}
}
private fun createFileChooser(multipleSelection: Boolean): JFileChooser {
return JFileChooser().apply {
currentDirectory = lastDirectory
fileSelectionMode = JFileChooser.FILES_ONLY
isMultiSelectionEnabled = multipleSelection
dialogTitle = messages.getString("dialogTitle")
fileFilter = VideoFileFilter()
}
}
private fun showEmptyFilesWarning(parentDir: File?) {
val message = if (parentDir != null) {
"Todos os arquivos selecionados em ${parentDir.absolutePath} estão vazios!\nSelecione outros arquivos."
} else {
"Nenhum arquivo selecionado."
}
notifier.showWarning(message)
}
private fun handleInvalidFiles(invalidFiles: List<File>): Boolean {
invalidFiles.forEach { file ->
logger.info("Selected file: ${file.absolutePath} is ${file.length()} bytes in size.")
}
val userChoice = notifier.confirmSmallFiles(
"Alguns arquivos selecionados são pequenos ou não são MP4.\nDeseja continuar?"
)
return userChoice
}
}
// Exceções personalizadas para maior clareza
class UserCancelledException(message: String) : Exception(message)
class InvalidFileException(message: String) : Exception(message)
// Implementação de um filtro de arquivos específico para vídeos (exemplo)
class VideoFileFilter : javax.swing.filechooser.FileFilter() {
override fun accept(file: File): Boolean {
return file.isDirectory || file.extension.lowercase() in listOf("mp4", "avi", "mkv", "mov")
}
override fun getDescription(): String {
return "Arquivos de Vídeo (*.mp4, *.avi, *.mkv, *.mov)"
}
}
// Interface adicional para notificações específicas (opcional)
interface UserNotifier {
fun showWarning(message: String)
fun confirmSmallFiles(message: String): Boolean
}
// Implementação de exemplo para UserNotifier
class JOptionPaneUserNotifier : UserNotifier {
override fun showWarning(message: String) {
javax.swing.JOptionPane.showMessageDialog(null, message, "Aviso", javax.swing.JOptionPane.WARNING_MESSAGE)
}
override fun confirmSmallFiles(message: String): Boolean {
val result = javax.swing.JOptionPane.showConfirmDialog(null, message, "Confirmação", javax.swing.JOptionPane.YES_NO_OPTION)
return result == javax.swing.JOptionPane.YES_OPTION
}
}
```
### Melhorias Implementadas
1. **Injeção de Dependências Clara**:
- **Construtor Primário**: Mantém todos os parâmetros necessários explicitamente, facilitando testes e manutenção.
- **Construtor Secundário**: Oferece valores padrão da mesma forma que o código original, mantendo a compatibilidade.
2. **Remoção de `exitProcess`**:
- **Exceções Personalizadas**: Em vez de encerrar o processo diretamente, lançamos exceções específicas (`UserCancelledException` e `InvalidFileException`). Isso permite que a camada superior gerencie o fluxo da aplicação de maneira mais controlada e evita efeitos colaterais inesperados.
3. **Uso de Loops ao Invés de Recursão**:
- **Loop `while (true)`**: Substitui a recursão para re-exibir o seletor de arquivos, evitando possíveis estouros de pilha e melhorando a legibilidade.
4. **Constantes Claras**:
- **`DEFAULT_MAX_FILE_SIZE`**: Substitui o número mágico `2_000_000_000` por uma constante com nome descritivo.
- **`DEFAULT_LAST_DIRECTORY`**: Define o diretório padrão de forma mais clara.
5. **Filtragem de Arquivos**:
- **`VideoFileFilter`**: Adiciona um filtro específico para tipos de arquivos de vídeo, melhorando a experiência do usuário ao selecionar apenas arquivos relevantes.
6. **Separação de Responsabilidades**:
- **Métodos Auxiliares**: Métodos como `showEmptyFilesWarning` e `handleInvalidFiles` encapsulam partes específicas da lógica, tornando o método principal mais limpo e focado.
- **Responsabilidade de Notificação**: A interface `UserNotifier` foi expandida para incluir mais comportamentos específicos, como confirmação para arquivos pequenos.
7. **Melhoria na Nomenclatura**:
- **Nomes Claros e Descritivos**: Parâmetros, métodos e classes agora têm nomes que refletem melhor suas responsabilidades e comportamentos, facilitando a compreensão do código.
8. **Tratamento Adequado de Arquivos Selecionados**:
- **Filtragem de Arquivos Vazios**: Continua presente, mas de forma mais clara e centralizada.
- **Verificação de Arquivos Inválidos**: A lógica para lidar com arquivos pequenos ou com extensões incorretas está mais clara e separada.
9. **Adição de `FileFilter` Específico**:
- **`VideoFileFilter`**: Melhora a experiência do usuário ao restringir os tipos de arquivos exibidos no seletor, seguindo o princípio de **Single Responsibility**.
10. **Interface `UserNotifier` Expandida**:
- **Método `confirmSmallFiles`**: Permite uma confirmação mais específica em casos de arquivos potencialmente inválidos, seguindo o princípio de **Interface Segregation**.
### Benefícios das Melhorias
- **Manutenibilidade**: Código mais modular e responsabilidades claramente definidas facilitam futuras alterações e adições.
- **Testabilidade**: Com classes melhor segregadas e dependências injetadas, torna-se mais simples escrever testes unitários para cada componente.
- **Legibilidade**: Nomes descritivos e métodos menores melhoram a compreensão do fluxo de trabalho.
- **Robustez**: Tratamento adequado de exceções evita encerramentos inesperados da aplicação e permite um gerenciamento de erros mais eficaz.
- **Reutilização**: Componentes como `VideoFileFilter` e `UserNotifier` podem ser reutilizados ou facilmente substituídos por outras implementações conforme necessário.
### Considerações Finais
Aplicar os princípios **SOLID** e **Clean Code** não apenas melhora a qualidade do código atual, mas também prepara a base para um desenvolvimento mais escalável e sustentável no futuro. As refatorações acima visam tornar o `JFileChooserVideoFileSelector` mais robusto, flexível e alinhado às melhores práticas de engenharia de software.