Vedere Testo del labotatorio, qui viene fornita la soluzione di alcuni degli esercizi.
Nota importante:
In generale la soluzione non e' unica.
Questa non e' LA soluzione, ma UNA possibile soluzione.
Fare in modo che le voci di menu' "non valide" in questo momento (cioe' "presta", "restituisci", "salva", "dismetti" in caso di biblioteca vuota) restino disabilitate.
Tali voci vanno disabilitate, previo controllo se la biblioteca e' vuota, nei seguenti momenti:
I pezzi di codice da inserire sono:
I luoghi dove vanno messi sono (tutti nella classe GestioneBiblioteca):
Fare si' che la finestra di dialogo per la restituzione mostri solo i libri che hanno qualche copia prestata.
Nota 1: il testo aveva un errore, diceva la finestra per "il prestito" invece che quella per la restituzione.
Nota 2: questo esercizio era dato per "breve" ma in realta' poi tanto breve non e'.
Vanno modificate le funzioni che, dentro la classe Dialog3 che realizza la finestra di dialogo per la restituzione, gestiscono l'avanzamento/arretramento nell'array di libri: vaiAvanti, vaiIndietro, avantiTutta, dietroTutta.
Tuttavia se le cambiassimo nella classe Dialog3 allora avremmo questo comportamento modificato anche nelle finestre di dialogo per dismissione e per prestito, poiche' anche queste sono realizzate con la classe Dialog3.
Allora definiamo una nuova classe Dialog3bis, sotto-classe di Dialog3: la finestra di dialogo per restituzione, e solo quella, sara' adesso un oggetto di classe Dialog3bis invece che Dialog3; le altre due finestre di dialogo restano di classe Dialog3.
La classe Dialog3bis deve contenere solo la nuova implementazione delle 4 funzioni vaiAvanti, vaiIndietro, avantiTutta, dietroTutta. Non deve ripetere tutto il contenuto di Dialog3 poiche' questo viene ereditato.
Nella biblioteca vi e' un array di libri, che va dalla posizione 0 alla posizione biblioteca.numeroLibri()-1. Nella finestra c'e' un attributo intero codiceCorrente che tiene una posizione dentro questo array. Un caso speciale e' dato dalla biblioteca vuota, dove il valore di codiceCorrente deve essere e restare -1 (= nessun libro).
Le 4 funzioni, nella versione attuale ereditata da Dialog3, fanno questo:
Nella nuova versione dovranno eseguire un simile scorrimento su
un sotto-array virtuale fatto da un sotto-insieme delle celle di quello
reale: tutte e sole le celle occupate da libri che hanno qualche
copia prestata.
Nota: questo sotto-array vistuale potrebbe essere vuoto, anche se
l'array reale non e' vuoto (succede quando nessun libro ha al momento
copie prestate).
Definiamo due funzioni ausiliarie cercaAvanti e cercaIndietro che cercano, a partire da una certa posizione, la prossima posizione, rispettivamente in avanti e indietro, occupata da un libro che ha copie prestate; se non esiste, ritornano -1.
int cercaAvanti(int partenza) { // p conterra' il risultato int p = partenza; // vado avanti finche' non sono infondo e finche' il libro // corrente non ha copie prestate while ( (p0) && (biblioteca.libroPerCodice(p).copiePrestate()==0) ) p--; // a questo punto guardo se ho trovato un libro corrente // con copie prestate if (biblioteca.libroPerCodice(p).copiePrestate()!=0) return p; // altrimenti non ce ne sono else return -1; }
Le due funzioni vaiAvanti e indietroTutta usano la funzione ausiliaria cercaAvanti:
void vaiAvanti() { int tentativo; // mettero' qui la posizione dove andare // se sono gia' alla fine, niente if (codiceCorrente>=biblioteca.numeroLibri()-1) tentativo = -1; // altrimenti cerco avanti else tentativo = cercaAvanti(codiceCorrente+1); if (tentativo!=-1) // se ho trovato lo metto { codiceCorrente = tentativo; mostraLibro(codiceCorrente); } // altrimenti codiceCorrente resta quello che era } void indietroTutta() { int tentativo; // mettero' qui la posizione dove andare // se biblioteca vuota, niente if (biblioteca.numeroLibri()==0) tentativo = -1; // altrimenti cerco avanti partendo dall'inizio else tentativo = cercaAvanti(0); // si fa la stessa cosa anche se e' venuto -1, // in questo caso mettera' il libro nullo codiceCorrente = tentativo; mostraLibro(codiceCorrente); }
Le due funzioni vaiIndietro e avantiTutta usano la funzione ausiliaria cercaIndietro:
void vaiIndietro() { int tentativo; // mettero' qui la posizione dove andare // se sono gia' all'inizio, niente if (codiceCorrente<=0) tentativo = -1; // altrimenti cerco indietro else tentativo = cercaIndietro(codiceCorrente-1); if (tentativo!=-1) // se ho trovato lo metto { codiceCorrente = tentativo; mostraLibro(codiceCorrente); } // altrimenti codiceCorrente resta quello che era } void avantiTutta() { int tentativo; // mettero' qui la posizione dove andare // se biblioteca vuota, niente if (biblioteca.numeroLibri()==0) tentativo = -1; // altrimenti cerco indietro partendo dalla fine else tentativo = cercaIndietro(biblioteca.numeroLibri()-1); // si fa la stessa cosa anche se e' venuto -1, // in questo caso mettera' il libro nullo codiceCorrente = tentativo; mostraLibro(codiceCorrente); }
Inoltre la classe Dialog3bis dovra' definire il suo costruttore, che semplicemente richiamera' quello della super-classe:
public Dialog3bis(Frame f, String title, Biblioteca bib, boolean modal) { super(f,title,bib,modal); }
Fare si' che non sia possibile prestare l'ultima copia di un libro (cioe' quando numero di copie prestate == numero di copie totali - 1).
La soluzione e' semplicissima. Modificare la funzione actionPerformed della classe interna EseguiPrestito in GestioneBiblioteca. Subito prima della linea:
biblioteca.prestaLibro(dPresta.codiceScelto());Inserire controllo se il libro individuato dal codice scelto ha numero di copie prestate uguale a tutte tranne una. In caso affermativo sollevare eccezione (provochera' comparsa della finestra di dialogo e non esecuzione del prestito):
LibroInBiblioteca l = biblioteca.libroPerCodice(dPresta.codiceScelto()); if (l.copieTotali()-l.copiePrestate()==1) throw new Exception("Non posso prestare ultima copia");
Scrivere in rosso i dati dei libri non prestabili nella finestra per il prestito e in verde i dati dei libri non restituibili nella finestra per la restituzione.
Anche qui, come nell'esercizio 2, bisogna definire sotto-classi della classe Dialog3:
Entrambe queste sotto-classi devono contenere solo la funzione mostraLibro (con la nuova implementazione) e il costruttore (che richiama il costruttore della super-classe con gli stessi parametri, ved. come fatto nella soluzione dell'esercizio 2).
Ecco qui la nuova implementazione della funzione mostraLibro in Dialog3Presta (in Dialog3Restit e' analoga - cambia solo il colore e la condizione per usarlo).
public void mostraLibro(int cod) { LibroInBiblioteca l = null; if (biblioteca!=null) l = biblioteca.libroPerCodice(cod); if (l==null) pulisci(); else { codiceCorrente = cod; /*inizio parte aggiunta*/ if (l.copiePrestate()==l.copieTotali()) { tAut.setForeground(Color.red); tTit.setForeground(Color.red); tCop.setForeground(Color.red); } else { tAut.setForeground(Color.black); tTit.setForeground(Color.black); tCop.setForeground(Color.black); } /*fine parte aggiunta*/ tAut.setText(l.autore); tTit.setText(l.titolo); tCop.setText(l.copieTotali() + " (" + l.copiePrestate() + ")"); } }Nota: e' necessario mettere il colore nero quando il libro e' prestabile, altrimenti rimarrebbe il rosso impostato quando e' stato precedentemente mostrato un libro non prestabile.
Aggiungere alla finestra Dialog3 in alto (nello stesso pannello dei bottoni)
un bottone "cerca" e un campo di testo in cui l'utente puo' digitare
la stringa con cui deve iniziare il titolo del libro.
Aggiungere la callback del bottone "cerca" che
Nella classe Dialog3 eseguire le seguenti modifiche:
1)
Aggiungere gli attributi (con questi o altri nomi):
Button bCerca;
TextField tDaCercare;
2)
Nella funzione "costruisci" il pannello pSopra ha grid layout
1x6 invece che 1x4
sempre nella funzione "costruisci", creare e aggiungere i due
nuovi componenti nel pannello pSopra:
pSopra.add(bCerca = new Button("Cerca:"));
pSopra.add(tDaCercare = new TextField(""));
3) Scrivere la funzione che costituira' il comportamento del bottone bCerca (analogamente alle funzioni che costituiscono i comportamenti degli altri 4 bottoni):
void cercaTitolo() { // prende la stringa che l'utente ha digitato nel campo String s = tDaCercare.getText(); // inizia ciclo sui libri int i; int trovato = -1; // codice del libro trovato for (i=0; i4) Nel costruttore di Dialog3, assegnare al bottone bCerca action listener che esegue la funzione cercaTitolo sopra definita, in modo analogo a come viene fatto con gli altri bottoni:
bCerca.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { cercaTitolo(); } });Esercizio 6
Richiesta
Aggiungere al menu' generale una voce "salva con nome". Associare a questa voce una callback (event listener) che fa apparire una finestra di dialogo che chiede un nome di file.
Soluzione
La finestra che chiede il nome del file e' realizzata dalla classe Dialog5, sotto-classe di Dialog1. Rispetto a Dialog1, essa:
- aggiunge attributo TextField tNomefile campo di testo dove l'utente digitera' il nome del file;
- ridefinisce la funzione costruisci, la quale fa tutto cio' che faceva nella super-classe Dialog1, piu' creare e mettere al centro il pannello pCentro che contiene il text field tNomefile e un'etichetta esplicativa, per esempio sistemati tramite un border layout di cui occupano rispettivamente le parti sinistra e centrale;
- definisce il suo costruttore, che fa esattamente cio' che faceva nella super-classe (ma e' cambiata l'implementazione della funzione costruisci).
Il codice di questa nuova classe e':
class Dialog5 extends Dialog1 { public TextField tNomefile; protected void costruisci() { super.costruisci(); // fa cio' che faceva in Dialog1 pCentro = new Panel(new BorderLayout()); tNomefile = new TextField(20); // campo testo lungo 20 caratteri pCentro.add(tNomefile, BorderLayout.CENTER); pCentro.add(new Label("Nome file:"), BorderLayout.WEST); add(pCentro, BorderLayout.CENTER); // aggiunge alla finestra } Dialog5(Frame f, String title, boolean modal) { super(f,title,modal); } }Nella classe GestioneBiblioteca, abbiamo una finestra di dialogo dSalva, di classe Dialog5, che compare quando l'utente seleziona la voce "salva". Il bottone Ok di questa finestra salvera' sul file il cui nome e' stato digitato nella finestra dall'utente.
Ricalcare quanto viene fatto per le altre voci di menu' che fanno comparire finestre di dialogo:
- aggiungere attributo Dialog5 dSalva
- nel metodo "costruisci" creare dSalva come nuova finestra di classe Dialog5
- associare alla voce di menu' iSalva lo show listener della finestra dSalva
- associare al bottone Ok di dSalva (tramite la funzione addOkListener) un action listener che: prende da dSalva.tNomefile il nome del file, controlla che non sia la stringa vuota (nel qual caso solleva eccezione), e poi esegue quello che fa l'attuale action listener della voce iSalva, ma usando il nome del file appena prelevato da dSalva.tNomefile invece che quello memorizzato in fileName; infine nasconde la finestra dSalva. In caso di eccezioni mostra finestra di errore.