Computación Web (2025/26)

Proyecto: tienda de merchandising

Introducción

En este proyecto desarrollarás la aplicación Web de una tienda en línea de merchandising. La tienda estará especializada en un sector comercial concreto de tu elección, como por ejemplo camisetas de equipos de fútbol, figuras de acción de personajes de películas, ropa y otros productos promocionales de grupos musicales, etc.

En la aplicación habrá dos tipos de usuarios: clientes y administradores. Los administradores podrán gestionar el catálogo de productos de la tienda (añadir nuevos productos, publicarlos para hacerlos visibles a los clientes o retirarlos para que dejen de ser visibles). Los clientes podrán navegar el catálogo de productos, añadir productos a su carro de la compra, examinar su carro de la compra y vaciarlo.

Los administradores deben poder añadir al catálogo productos fijos y productos no fijos. Los productos fijos son aquellos que el cliente no puede personalizar o configurar de ninguna forma (por ejemplo, una taza con diseño y tamaño fijos). Los productos no fijos son aquellos en que a los clientes se les ofrecen diversas opciones para elegir, o se les ofrece establecer un mensaje personalizado. Son ejemplos de productos no fijos los siguientes:

El tipo de personalización escogido podría tener efectos en el precio del producto. Por ejemplo:

Además de escoger un sector comercial concreto para tu tienda, puedes decidir entre hacerla más general (por ejemplo, camisetas o merchandising de distintos equipos de fútbol) o más específica (por ejemplo, merchandising variado, como camisetas, chándales, balones, mochilas, etc,) de un único equipo de fútbol. En todo caso, debes evitar acotar en exceso el ámbito de tu tienda. Por ejemplo, no se puede desarrollar una tienda que solo venda camisetas de un único equipo de fútbol.

El proyecto se dividirá en funcionalidad obligatoria, que toda entrega debe implementar, funcionalidad adicional y funcionalidad avanzada. Tienes libertad para decidir qué funcionalidad adicional y avanzada deseas implementar en tu proyecto.

Condiciones generales

Las siguientes condiciones aplican al proyecto:

Funcionalidad obligatoria

Cualquier funcionalidad que no se mencione expresamente en esta sección no es obligatoria. La funcionalidad no obligatoria que implementes será evaluada como funcionalidad adicional o funcionalidad avanzada.

Todas las entregas deben implementar la siguiente funcionalidad:

Productos no fijos

Cuando creen un producto no fijo, los administradores podrán definir las opciones disponibles. Para cada uno de estos productos, el administrador podrá crear campos de personalización de distintos tipos. Los tipos de campo de personalización a implementar dependerán del tipo concreto de productos que ofrezca tu tienda. Debes incluir, como mínimo, la posibilidad de añadir campos de los dos tipos siguientes:

  • Campo de selección entre varias opciones: el cliente debe elegir una opción entre varias disponibles. Por ejemplo, en el caso de una sudadera, se podrían definir tres campos distintos para seleccionar un color, una talla y un diseño. Cuando el administrador defina un campo de este tipo, deberá introducir los posibles valores para el campo, de entre los cuales el cliente deberá seleccionar uno (por ejemplo, para el campo de talla, los posibles valores podrían ser S, M, L y XL). Otro ejemplo de este tipo de campo sería aquel que permita a un cliente elegir si desea un libro firmado o sin firmar.
  • Campo de texto: el cliente puede introducir un valor de texto. Por ejemplo, en el caso de una camiseta de fútbol personalizada, donde el usuario pueda elegir un nombre y un número para imprimir en la camiseta, el administrador definiría un campo de texto para el nombre y otro campo de texto para el número. Los administradores podrán restringir el formato del texto a introducir en un campo de este tipo mediante una expresión regular de Java (véase Uso de expresiones regulares para validar datos). Los administradores podrán también marcar un campo de este tipo como opcional, de tal forma que los clientes no estén obligados a introducir un valor para el mismo, o como obligatorio, en cuyo caso sí lo estaría.

Cada campo de personalización tendrá una etiqueta asociada como, por ejemplo, "color", "talla", "diseño", "nombre a imprimir" o "número a imprimir".

Por otra parte, distintas opciones de personalización podrían tener distintos efectos en el precio del producto. Para dar soporte a esto, para cada producto se definirá un precio base. En los campos de personalización de selección entre varias opciones, los administradores asociarán a cada opción un precio adicional a sumar al precio base. El precio adicional puede depender de la opción concreta, y para algunas opciones este puede ser cero. En los campos de personalización de texto opcionales, los administradores podrán definir un precio adicional fijo a sumar al precio base si los clientes introducen un valor para el campo. Si el campo es obligatorio, lo razonable es que definan cero como precio adicional, dado que se entiende que el precio base ya incluye la personalización de dicho campo.

Cuando a un cliente se le presente la vista de un producto no fijo, se le presentará también un formulario para que seleccione un valor para cada uno de los campos de personalización definidos para dicho producto. Para cada campo se mostrará su etiqueta en el formulario. La aplicación debe actualizar el precio del producto dinámicamente a medida que el cliente vaya rellenando el formulario, usando JavaScript para ello.

Cuando a un usuario se le muestre su carro de la compra, se le mostrará, para cada campo de personalización, la etiqueta del campo, el valor elegido por el cliente y el precio adicional asociado a dicho valor.

Modelo de datos

Tu proyecto debe dar soporte al siguiente modelo de datos para la funcionalidad obligatoria. Puedes desviarte de este modelo si tienes razones justificadas para hacerlo. Por ejemplo, puedes enriquecer este modelo de datos debido a las necesidades de cualquier funcionalidad adicional que implementes (añadir nuevas entidades, añadir nuevas propiedades a algunas entidades, etc.), o si crees que otro modelo diferente es mejor en el contexto de tu diseño del proyecto.

Las principales entidades a almacenar en la base de datos son:

  • Usuarios: se identifica a los usuarios por su dirección de correo electrónico, que será única en la aplicación. Cada usuario se autentica mediante dicha dirección de correo electrónico y una contraseña, que debe ser almacenada en la base de datos de forma segura, cifrada y con valor de salt (por ejemplo, usando bcrypt como en las prácticas). Se deben guardar también, al menos, el nombre y apellidos del usuario. Los usuarios pueden ser de tipo cliente o administrador.
  • Productos: para cada producto se guardarán un nombre, una descripción y un precio base. Además, habrá otros campos con información específica, que dependerán del tipo de productos que ofrezca tu tienda. Por ejemplo, si se trata de una tienda de libros, se podrían guardar el autor, la editorial, el número de páginas, el ISBN, etc. Dispondrán también de un campo para indicar si el producto está o no publicado, esto es, si es visible para los clientes o no.
  • Imágenes de productos: para cada producto se guardará una imagen. Desde el punto de vista de la funcionalidad obligatoria, existe una relación uno a uno entre productos e imágenes de productos (un producto tiene una imagen y una imagen corresponde a un solo producto).
  • Campos de personalización: para cada campo de personalización se incluyen su etiqueta y el tipo de campo de personalización (por ejemplo, selección entre varias opciones o campo de texto). Se incluyen también atributos específicos de cada tipo de campo de personalización si procede. Por ejemplo, los campos de texto necesitan un atributo para indicar si son opcionales u obligatorios, otro para la expresión regular que restringe el formato del texto a introducir y otro para el precio adicional a sumar al precio base del producto si el cliente introduce un valor para el campo. Existe una relación uno a muchos entre productos y campos de personalización (un producto puede tener varios campos de personalización y un campo de personalización está ligado a exactamente un producto).
  • Opciones de campos de personalización: para cada campo de personalización de selección entre varias opciones, se guardará cada opción con su etiqueta textual (la que se mostrará al cliente en la lista de opciones) y su precio adicional. Existe una relación uno a muchos entre campos de personalización y opciones de campos (un campo de personalización puede tener varias opciones, y una opción está ligada a exactamente un campo de personalización).
  • Producto seleccionado: para cada producto que un cliente añada a su carro o a una compra, se guardará el producto seleccionado y el número de unidades añadidas o compradas. Para los productos no fijos, se guardará también el valor seleccionado para cada uno de sus campos de personalización (se trata de una relación uno a muchos con los campos de personalización completados, según lo explicado en el siguiente punto).
  • Campo de personalización completado: para cada campo de personalización que un cliente complete, se guardará el valor introducido por el cliente. Existe una relación muchos a uno entre campos de personalización completados y productos seleccionados (un producto seleccionado puede tener varios campos de personalización completados, y un campo de personalización completado está ligado a exactamente un producto seleccionado). Se puede crear una única entidad de este tipo con un atributo que represente una relación con la opción de campo de personalización elegida, para rellenar en el caso de los campos de selección entre varias opciones (relación muchos a uno con las opciones de campos de personalización), y otro atributo distinto que represente el texto introducido por el cliente, para rellenar en el caso de los campos de texto. Cuando se complete un campo de personalización de selección entre varias opciones, se guardará (como relación) la opción de campo de personalización elegida por el cliente, y se pondrá a NULL el otro atributo. Por el contrario, si se trata de un campo de texto, se establecería el valor NULL en la relación anterior. El atributo del texto introducido tomaría también valor NULL para campos opcionales no seleccionados, y un texto para campos obligatorios o para campos opcionales que el cliente haya decidido seleccionar.
  • Carro de la compra: el carro de la compra de cada usuario estará compuesto por diversos productos seleccionados. Se trata de una relación uno a muchos (un carro de la compra puede contener varios productos seleccionados, pero un producto seleccionado solo puede estar en un carro de la compra).

Vistas

Tu aplicación debe proporcionar al menos las siguientes vistas:

  • Vista principal (abierta también a usuarios no autenticados): la composición de esta vista es libre, con el único requisito de que debe dar acceso a las funcionalidades de la tienda. Por ejemplo, se pueden presentar productos destacados de la tienda, un formulario de búsqueda de productos, campañas comerciales actuales, etc.
  • Vista de creación de producto (abierta a usuarios de tipo administrador autenticados): se presenta a los administradores un formulario para crear un nuevo producto fijo o no fijo. Si el producto es de tipo no fijo, el administrador puede crear uno o más campos de personalización.
  • Vista de producto (abierta también a usuarios no autenticados): se presenta la información detallada de un producto, incluyendo su precio. Para los productos no fijos, se muestra también el formulario para seleccionar las opciones de personalización (se proporciona una pista acerca de cómo recoger estos parámetros a continuación, en el método del controlador, en Recogida de parámetros de la petición con nombre no conocido) y se informa de cómo estos afectan al precio del producto. Se debe actualizar dinámicamente mediante JavaScript el precio mostrado al cliente según este vaya haciendo en el formulario. Si el usuario está autenticado y es cliente, se mostrará un botón para añadir el producto al carro y un control para seleccionar el número de unidades a añadir. Si el usuario autenticado es administrador, se mostrará un botón para publicar o retirar el producto, según corresponda.
  • Vista de carro de la compra (abierta solo a usuarios autenticados): se presenta el contenido actual del carro de la compra del usuario. Se permitirá al usuario eliminar un producto del carro de la compra o vaciarlo por completo con una sola acción. Estas acciones eliminarán también de la base de datos, en cascada, todos los objetos de la entidad de productos seleccionados que haya en la base de datos relacionados con el carro de la compra. También se eliminarán de la base de datos, a su vez, todos los objetos de la entidad de campo de personalización completado ligados a dichos productos seleccionados.

Puedes tomar la lista de vistas anterior como una sugerencia. Eres libre de diseñar tu aplicación con vistas diferentes siempre y cuando proporciones la misma funcionalidad. También puedes cambiar estas vistas para acomodar funcionalidad adicional.

Funcionalidad adicional

El resto de la funcionalidad a desarrollar podrá ser elegida libremente por cada grupo, para obtener hasta 1,5 puntos adicionales conforme a los criterios de evaluación.

Una o dos funciones adicionales, dependiendo de su complejidad, podrían aportar la nota máxima de este apartado. Puedes consultar con los profesores en cualquier momento para saber a qué nota aspiras según lo que pretendas implementar.

Funcionalidad avanzada

Para obtener el punto de funcionalidad avanzada previsto en los criterios de evaluación debes hacer uso de otras tecnologías que no se hayan visto en clase o se hayan visto con menor profundidad.

Se proponen, a modo de referencia, algunas posibilidades a continuación, pero puedes implementar cualquier otra que pactes previamente con los profesores:

Cada aspecto avanzado se evaluará en función de la complejidad de su implementación, teniendo además en cuenta la dificultad técnica, documentación disponible, etc.

También puedes implementar otros aspectos avanzados propuestos por ti, siempre que supongan profundizar en alguna de las tecnologías vistas en clase o el uso de otras tecnologías nuevas. Debes consultar previamente con los profesores para saber si tu propuesta resulta adecuada y obtener una estimación de cómo se valoraría en tu calificación. No se valorará que desarrolles nueva funcionalidad que simplemente consista en utilizar las mismas técnicas que se usan en la funcionalidad obligatoria y adicional.

Uno de los aspectos que más se valorarán con respecto a esta funcionalidad avanzada es tu capacidad para utilizar la documentación disponible y resolver los problemas de forma autónoma. Por ello, contarás con un soporte más limitado por parte de los profesores que para la funcionalidad obligatoria y adicional.

Es muy aconsejable que, al principio del proyecto o durante el desarrollo del mismo, pidas a los profesores una estimación de la puntuación que obtendrías por la implementación de cada función adicional o avanzada que te plantees realizar. Esto te permitirá conocer qué debes desarrollar para alcanzar la puntuación a la que aspires.

Criterios de evaluación

Aviso: este proyecto es una prueba de evaluación. El plagio y otros tipos de fraude académico relativos al mismo serán denunciados a las autoridades académicas conforme a la normativa en vigor.

En la evaluación se tendrá en cuenta la correcta implementación de la funcionalidad obligatoria, funcionalidad adicional y funcionalidad avanzada, así como diversos aspectos de calidad de su implementación:

Para los equipos de 3 personas que hayan sido autorizados de forma excepcional, se fijan la puntuación máxima de la funcionalidad obligatoria en 6,5 puntos, de la funcionalidad adicional en 2,5 puntos y de la funcionalidad avanzada en 1 punto.

Los criterios de calidad que se espera que cumpla tu proyecto son:

En las clases de seguridad en aplicaciones Web se explicarán detalle estas y otras vulnerabilidades frecuentes, y cómo evitarlas.

Sugerencias de implementación

En esta sección se incluyen algunas sugerencias que pueden ser útiles para la implementación de tu proyecto.

Gestión de las imágenes de productos

De acuerdo con el enunciado, cuando un administrador cree un producto, podrá subir una imagen para el mismo. En este apartado se incluyen pistas para implementar esta funcionalidad, utilizando la base de datos de la aplicación para almacenar las imágenes.

Desde el punto de vista de la funcionalidad obligatoria, hay una relación uno a uno entre productos e imágenes de productos (cada producto tiene una imagen, y cada imagen está asociada a un único producto). Por ello, se puede crear una entidad de imágenes de productos de la siguiente forma:

@Entity
public class Photo {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    @Column(nullable = false)
    private String imageType;

    @Lob
    @Column(nullable = false, length = 256 * 1024)
    private byte[] imageData;

    @OneToOne
    @JoinColumn(name = "product_id")
    private Product product;

    // ...getters y setters...
}
Esta columna almacenará el tipo MIME de la imagen (por ejemplo, image/jpeg o image/png).
La anotación @Lob indica que el atributo es un objeto grande. Permite almacenar campos de gran tamaño en la base de datos, ya sean de tipo textual o binario.
El atributo length de la anotación @Column se utiliza para especificar el tamaño máximo permitido para el campo de la base de datos. En este caso, se establece un límite de 256 KB para cada imagen. El sistema de JPA elegirá el tipo de columna más adecuado conforme a este límite.
Esta columna almacenará los datos binarios de la imagen.
La anotación @OneToOne indica que existe una relación uno a uno entre la entidad de imagen y la entidad de producto. En este caso, cada imagen está asociada a un único producto, y cada producto tiene una única imagen.
La entidad de la imagen de producto será la propietaria de la relación. Por ello, será la que almacene la clave ajena que hace referencia al producto al que está asociada la imagen.

Además, debes añadir a la entidad del producto el atributo correspondiente al otro lado de la relación, con su correspondiente anotación de JPA. Puedes declararla como opcional en dicho lado, para permitir que, temporalmente, un producto no tenga imagen asociada (por ejemplo, justo después de haber sido creado). También debes declarar una interfaz de repositorio para esta nueva entidad.

Aunque no es obligatorio hacerlo así, se sugiere permitir a los administradores subir la imagen de un producto después de haberlo creado. De esta forma, también será posible que los administradores cambien la imagen del producto en cualquier momento. Se usará un formulario para subir la imagen de un producto, que podría estar incluido, por ejemplo, en la vista de un producto, mostrándose solo en el caso de que el usuario autenticado sea un administrador. Este debe incluir un control de tipo file, subir los datos con una petición de tipo POST y, además, codificar los datos con multipart/form-data. El motivo es que los controles de tipo file solo pueden subir el contenido del fichero con dicha combinación de método y codificación:

<form th:action="@{/photos/upload}" method="post" enctype="multipart/form-data">
    <input type="hidden" name="productId" th:value="${product.id}">
    <input type="file" name="photo" accept="image/*" required>
    <input type="submit" value="Upload photo">
</form>
El formulario incluye un campo oculto para enviar el identificador del producto al que se va a asociar la imagen. Su valor se establece dinámicamente con Thymeleaf, tomando el identificador del producto que se muestra en la vista.
El navegador permitirá al usuario seleccionar un fichero de su sistema local para subir al servidor.
El navegador codificará los datos del formulario utilizando el formato multipart/form-data, que es el formato adecuado para subir ficheros eficientemente. Solo se puede usar este tipo de codificación con el método POST, dado que los datos codificados de esta forma deben ser enviados necesariamente en el cuerpo de la petición HTTP.
El atributo accept del control de tipo file se utiliza para indicar al navegador que solo debe permitir seleccionar ficheros de tipo imagen.
Es obligatorio que el usuario seleccione un fichero para subir.

En el lado del servidor, necesitaremos un método de controlador que gestione las peticiones enviadas desde dicho formulario, y almacene la imagen en la base de datos. Además, necesitaremos otro método de controlador que devuelva estas imágenes desde la base de datos. Programaremos una nueva clase de tipo controlador dedicada exclusivamente a la gestión de las imágenes de productos (aunque nada impide que simplemente añadamos estos métodos a otro controlador ya existente) con dichos dos métodos:

@Controller
@RequestMapping(path = "/photos")
public class PhotoController {

    @Autowired
    private ProductRepository productRepository;

    @Autowired
    private PhotoRepository photoRepository;

    @PostMapping(path = "/upload")
    public String uploadPhoto(@RequestParam("photo") MultipartFile file,
                              @RequestParam("productId") Product product,
                              Principal principal) {
        User user = userRepository.findByEmail(principal.getName());
        // ...comprueba aquí que el usuario sea administrador...
        if (file.isEmpty()) {
            return "redirect:/product?id=" + product.getId() + "&error=empty";
        }
        if (file.getSize() > 256 * 1024) {
            return "redirect:/product?id=" + product.getId() + "&error=size";
        }
        if (product.getPhoto() != null) {
            photoRepository.delete(product.getPhoto());
        }
        Photo photo = new Photo();
        photo.setProduct(product);
        photo.setImageType(file.getContentType());
        try {
            photo.setImageData(file.getBytes());
        } catch (IOException e) {
            throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR,
                                              "Error processing the uploaded file", e);
        }
        photoRepository.save(photo);
        return "redirect:/product/" + product.getId();
    }

    @GetMapping(path = "/product/{productId}")
    public ResponseEntity<byte[]> getProductPhotos(
            @PathVariable("productId") Product product) {
        if (product.getPhoto() == null) {
            throw new ResponseStatusException(HttpStatus.NOT_FOUND,
                          "Photo not found for product id " + product.getId());
        }
        MediaType mediaType;
        if (photo.getImageType() != null) {
            mediaType = MediaType.parseMediaType(photo.getImageType());
        } else {
            mediaType = MediaType.APPLICATION_OCTET_STREAM;
        }
        return ResponseEntity.ok()
                .contentType(mediaType)
                .body(photo.getImageData());
    }
}
Se asigna el prefijo /photos a todas sus rutas de los métodos de este controlador. Por ejemplo, la ruta del método uploadPhoto será /photos/upload.
Este método responderá a la ruta /photos/upload (se concatena la ruta del controlador a la ruta del método) con método POST.
El parámetro de tipo MultipartFile se utiliza para recibir el fichero subido por el usuario. El nombre del control de tipo fichero en el formulario debe ser photo.
Se recoge el identificador del producto al que se va a asociar la imagen. Spring se encarga, automáticamente, de obtener de la base de datos el objeto de tipo Product correspondiente a dicho identificador.
Se comprueba que el usuario haya seleccionado un fichero para subir. Aunque se haya marcado como control obligatorio en el formulario, es necesario comprobarlo también en el lado del servidor, por seguridad.
Se comprueba que el tamaño del fichero no supere el límite de 256 KB establecido para las imágenes.
Si el producto ya tiene una imagen asociada, se elimina esta de la base de datos.
El navegador envía, normalmente, el tipo MIME del fichero subido, el cual puede ser obtenido con el método MultipartFile.getContentType().
Se obtienen los datos binarios del fichero subido, como array de bytes.
El tipo ResponseEntity otorga mayor control al método del controlador sobre la respuesta HTTP a devolver. Entre otros, permite establecer el código de estado, las cabeceras y el cuerpo de la respuesta.
El identificador del producto se recoge de la ruta de la petición HTTP. Spring se encarga, automáticamente, de obtener de la base de datos el objeto de tipo Product correspondiente a dicho identificador.
Se guarda en esta variable el tipo MIME de la imagen, contenido en una de las columnas de la tabla, para incluirlo como valor de la cabecera Content-Type.
Se crea una respuesta con estado 200 OK.
Se establece el tipo MIME de la respuesta HTTP.
Se establece el cuerpo de la respuesta HTTP con los datos binarios de la imagen.
Se redirige al usuario a la vista del mismo producto tras subir la imagen, para que pueda ver el resultado de su acción.

Para mostrar la imagen de un producto en la vista de dicho producto, se establecerá como ruta de dicha imagen la ruta del método getProductPhotos. Por ejemplo:

<img th:src="@{/photos/product/{productId}(productId=${product.id})}" alt="Product photo">

Recogida de parámetros de la petición con nombre no conocido

Para utilizar la anotación @RequestParam de Spring MVC, que recoge parámetros de la petición HTTP, es necesario conocer el nombre de dichos parámetros en el momento en que se programe el método del controlador. Sin embargo, en el caso de los campos de personalización de los productos no fijos, el nombre y el número de los mismos es desconocido cuando este se programa, porque depende del producto en concreto que se esté configurando.

Una opción para recoger estos parámetros es utilizar el objeto HttpServletRequest que define el API de Servlet de Java (recuerda que Spring MVC se construye sobre esta API). Este objeto se puede recibir como parámetro de un método de controlador. Por ejemplo:

@PostMapping(path = "/add-to-cart")
public String addToCart(@RequestParam("productId") Product product,
                        HttpServletRequest request,
                        Principal principal) {
    // ...
    for (Field field: product.getFields()) {
        String fieldValue = request.getParameter("field-" + field.getId());
        if (fieldValue != null) {
            // ...guarda el valor del campo de personalización...
        } else {
            // ...el usuario no ha rellenado el campo de personalización...
        }
        // ...
    }
    // ...
}
El objeto HttpServletRequest representa la petición HTTP que el cliente ha enviado al servidor. Permite acceder a toda la información de la petición, incluyendo los parámetros enviados por el cliente. Spring proporciona este objeto automáticamente a los métodos de controlador que lo declaren como parámetro.
El método getParameter del objeto HttpServletRequest se utiliza para recoger el valor de un parámetro de la petición HTTP a partir de su nombre. En este caso, asumimos que el nombre del parámetro se construye dinámicamente a partir del identificador del campo de personalización. Obviamente, es necesario que la plantilla Thymeleaf que genera el formulario utilice el mismo esquema de nombrado para los controles de los campos de personalización.

Uso de expresiones regulares para validar datos

El enunciado de la funcionalidad obligatoria establece que, para los campos de personalización de tipo texto, el administrador puede opcionalmente definir una expresión regular que restrinja los posibles valores que los clientes pueden introducir (por ejemplo, para que solo puedan introducir dígitos, solo letras y espacios, etc.). Java proporciona soporte para trabajar con expresiones regulares, por lo que incorporar esta funcionalidad en tu proyecto no debería ser complicado.

El administrador puede introducir cualquier expresión regular que sea válida en Java (véase la documentación acerca de expresiones regulares en Java). Algunos ejemplos de expresiones regulares válidas son los siguientes:

  • \d\d?: solo se pueden introducir uno o dos dígitos (números del 0 al 99), por ejemplo, para personalizar el número de una camiseta de fútbol.
  • [a-zA-Z ]+: solo se pueden introducir letras y espacios, por ejemplo, para personalizar el nombre que aparecerá en una camiseta.
  • [a-zA-Z0-9 ]{1,32}: solo se pueden introducir letras, dígitos y espacios, con mínimo un carácter y máximo 32.

La comprobación de si una determinada cadena de texto cumple o no una expresión regular se realiza invocando el método matches de la clase String de Java, que recibe la expresión regular como parámetro (véase la documentación de String.matches).

Cuando recibas desde el formulario una expresión regular introducida por el administrador, puedes comprobar que sea válida mediante el método compile de la clase Pattern de Java:

try {
    Pattern.compile(regex);
} catch (PatternSyntaxException e) {
    // ...la expresión regular no es válida...
}
El método compile es un método estático de la clase Pattern. Devuelve un objeto Pattern, que en este caso no necesitamos y, por ello, no guardamos.
Si la expresión regular no es válida, el método compile lanzará una excepción PatternSyntaxException.

Más detalles sobre el uso de herramientas de inteligencia artificial generativa

El uso de inteligencia artificial generativa está permitido en el desarrollo del proyecto, siempre que no incluyas grandes fragmentos de código generados por una inteligencia artificial en tu entrega.

Todos los equipos deben adjuntar a su entrega una declaración acerca del uso de inteligencia artificial generativa que incluya, al menos, la siguiente información:

Algunos ejemplos de usos recomendados de inteligencia artificial generativa son:

Se recomienda que siempre revises cualquier fragmento de código generado por una inteligencia artificial antes de incluirlo en tu proyecto, y que te asegures de entender cómo funciona cualquier fragmento de código que incluyas. Además, no confíes ciegamente en los fragmentos de código que la herramienta de inteligencia artificial generativa produzca. Pueden contener errores o hacer algo diferente de lo que necesitas.

Por último, recuerda que tendrás que programar en el examen final, en papel, modelos de datos, consultas a la base de datos, funciones de controlador, plantillas, etc. similares a los del proyecto, y que se exige una nota mínima en dicho examen. Independientemente de cómo uses la inteligencia artificial generativa, asegúrate de ser capaz de programarlos sin ayuda.