Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/efrain-svg/Potes_Freddy_ProgInterfacesG_U3/llms.txt

Use this file to discover all available pages before exploring further.

Contact Management App enforces a strict Model-View-Controller separation using Java package boundaries. The modelo package contains classes with no Swing imports, the vista package owns all Swing component construction, and the controlador package is the only place where listeners are attached and business rules are applied. This page explains how each layer is defined and how they connect at runtime.

The three layers

Model

modelo/persona.java — entity
modelo/personaDAO.java — data access
No Swing imports. Pure data and file I/O.

View

vista/ventana.java — main window
Builds and lays out all Swing components. Exposes them as public fields. Contains no listeners or business logic.

Controller

controlador/logica_ventana.java
Implements ActionListener, ListSelectionListener, ItemListener. Wires all events, manages concurrency, calls the DAO.

Model layer

The model layer consists of two classes that have no knowledge of the UI. persona.java is a plain entity that holds a contact’s data and knows how to serialize itself:
  • datosContacto() — returns a semicolon-delimited string for CSV storage
  • comoFilaTabla() — returns an Object[] that a JTable model can consume directly
personaDAO.java encapsulates all file-system interaction:
  • Reads and writes c:/gestionContactos/datosContactos.csv
  • Handles both the legacy 5-column format and the current 6-column format
  • Provides export overloads for custom column headers
  • Uses normalizarCategoria() to map multilingual display names to stable category codes
Neither class imports javax.swing or java.awt. If the storage format changes to a database in the future, only personaDAO needs to change.

View layer

ventana.java is responsible for constructing the entire UI and nothing else. Its main() method is the application entry point:
ventana.java
public static void main(String[] args) {
    ThemeManager.setupLookAndFeel();   // apply FlatLaf before any component is created

    EventQueue.invokeLater(new Runnable() {
        public void run() {
            new ventana().setVisible(true);
        }
    });
}
The constructor sets the window title, size (1180×740), and minimum size (1100×700), then delegates to two builder methods:
  • construirTabContactos() — assembles the Contacts tab: form fields, action buttons, filter bar, JTable, and status bar using BorderLayout and GridBagLayout
  • construirTabEstadisticas() — assembles the Statistics tab: summary stat cards, BarChartPanel, and PieChartPanel

Public field exposure

Rather than generating getter methods for every interactive component, ventana declares them as public fields. This is intentional: all event wiring is centralized in the controller, so the view only needs to make components reachable.
ventana.java
// Form inputs — the controller reads these to build a persona
public JTextField txt_nombres;
public JTextField txt_telefono;
public JTextField txt_email;
public JComboBox<String> cmb_categoria;
public JCheckBox chb_favorito;

// Action buttons
public JButton btn_add;
public JButton btn_modificar;
public JButton btn_eliminar;
public JButton btn_exportar;

// Filter / search
public JTextField txt_buscar;
public JComboBox<String> cmb_filtro_categoria;

// Table
public JTable tbl_contactos;
public DefaultTableModel modeloTabla;

// Status
public JLabel lbl_estado;
The view never attaches a listener to any of these components. That responsibility belongs exclusively to the controller.

Controller layer

logica_ventana.java receives a ventana instance (called delegado) in its constructor and uses it to reach every public field. It also holds the DAO, the i18n helper, the contact list, and all concurrency primitives.
logica_ventana.java
public class logica_ventana implements ActionListener, ListSelectionListener, ItemListener {

    private final ventana delegado;
    private final personaDAO dao;
    private final I18n i18n;
    private List<persona> contactos;

    // Concurrency
    private final Object contactosLock = new Object();   // plain monitor for list access
    private final Object exportLock    = new Object();   // plain monitor for CSV writes
    private final ReentrantLock editLock = new ReentrantLock(); // tryLock for edit ops
    private final ExecutorService exportExecutor       = Executors.newFixedThreadPool(2);
    private final ExecutorService notificationExecutor = Executors.newSingleThreadExecutor();
    private final AtomicInteger searchSeq  = new AtomicInteger(0);
    private final AtomicInteger busyCount  = new AtomicInteger(0);
}

Event wiring: configurarEventos()

All listeners are attached in a single method called during construction. The view’s public fields are used directly:
logica_ventana.java
private void configurarEventos() {
    // Button actions
    delegado.btn_add.addActionListener(this);
    delegado.btn_eliminar.addActionListener(this);
    delegado.btn_modificar.addActionListener(this);
    delegado.btn_exportar.addActionListener(this);

    // Combo boxes and checkbox
    delegado.cmb_categoria.addItemListener(this);
    delegado.cmb_filtro_categoria.addItemListener(this);
    delegado.cmb_idioma.addItemListener(this);
    delegado.chb_favorito.addItemListener(this);

    // Table row selection
    delegado.tbl_contactos.getSelectionModel()
        .addListSelectionListener(this);
}
Additional setup methods organize related configuration:
MethodResponsibility
configurarTablaYFiltro()Attaches TableRowSorter, DocumentListener for live search, and mouse listener for double-click edit
configurarAtajos()Registers InputMap/ActionMap bindings for Ctrl+S (save), Ctrl+N (new), Delete, Ctrl+E (edit), Ctrl+F (focus filter)
configurarMenuContextual()Builds right-click popup menu with edit and delete actions
aplicarIdioma(String)Fetches translated strings from I18n and updates all component labels without rebuilding the UI

CRUD methods

The controller owns all business operations:
1

Load contacts

cargarContactosRegistrados() — launches a SwingWorker that calls dao.leerArchivo() on a background thread, then populates modeloTabla on the EDT in done().
2

Add a contact

agregarContacto() — reads form fields from delegado, constructs a persona, calls validarContactoAsync() for a duplicate check, then calls dao.escribirArchivo() and refreshes the table.
3

Edit a contact

modificarContactoSeleccionado() — reads the selected row, pre-fills form fields via delegado, lets the user edit, then calls dao.actualizarContactos(contactos) which rewrites the entire file.
4

Delete a contact

eliminarSeleccionado() — removes the entry from the in-memory list and calls dao.actualizarContactos(contactos) to persist the deletion.
5

Export CSV

exportarCsv() — submits a task to exportExecutor that acquires exportLock and calls the appropriate dao.exportarCsv() overload.

Async search: solicitarBusquedaAsync()

The search field uses an AtomicInteger sequence number to discard stale results from previous keystrokes:
logica_ventana.java
private void solicitarBusquedaAsync() {
    final int seq = searchSeq.incrementAndGet();   // stamp this request

    if (searchWorker != null && !searchWorker.isDone()) {
        searchWorker.cancel(true);
    }

    searchWorker = new SwingWorker<RowFilter<Object, Object>, Void>() {
        @Override
        protected RowFilter<Object, Object> doInBackground() {
            // builds combined text + category RowFilter off the EDT
            String texto = delegado.txt_buscar.getText().trim();
            // ... build and return combined filter
            return null;
        }
        @Override
        protected void done() {
            if (isCancelled() || seq != searchSeq.get()) return;
            SwingUtilities.invokeLater(new Runnable() {
                @Override public void run() { sorter.setRowFilter(/* filtroFinal */null); }
            });
        }
    };
    searchWorker.execute();
}
This pattern—stamping async tasks with a monotonically increasing sequence number—prevents older, slower searches from overwriting the results of a newer, faster one.

Benefits of this separation

BenefitHow it is achieved
Testable modelpersona and personaDAO have no Swing dependencies and can be unit-tested without a display
Single place for event wiringAll listeners are added in configurarEventos() — no listener logic scattered across UI builder methods
UI rebuild without logic changesventana can be completely redesigned as long as its public field names remain stable
Locale switching at runtimeaplicarIdioma() updates labels via I18n without reconstructing any component
Responsive UI under loadAll blocking operations use SwingWorker or ExecutorService, never blocking the EDT

Architecture overview

Package structure, technology stack, and the full list of key design decisions.

Contact data model

persona fields, constructors, CSV serialization, and personaDAO method reference.

Build docs developers (and LLMs) love