Oblig 4 (INF1000 - V?r 2010)

Filmregister
M?l: I denne oppgaven vil du l?re hvordan man kan behandle en st?rre mengde data p? en objektorientert m?te, og f? trening i hele pensum.  Oppgaven kombinerer alle programmerings-elementer vi har sett tidligere i kurset, og innf?rer datastrukturen "HashMap".

Leveringsfrist

Fredag 7. mai kl. 16.00.  Leveres via Joly-systemet.  (Joly kan brukes hjemmefra vha. VPN eller lignende.)

Leveringskrav

L?sningen du lager skal v?re objektorientert.  Det betyr at programmet skal bruke objekter av klasser du selv definerer, og interaksjon mellom disse for ? l?se de forskjellige deloppgavene.  Besvarelsen m? bruke minst 3 klasser og 2 HashMap-er, men du kan godt utvide dette med flere klasser, HashMap-er, og andre datastrukturer etter ?nske.  Du skal bare levere to ting: Programmet ditt skal lese inn data fra to datafiler.  Disse skal du ikke levere.

Oppgaven skal l?ses individuelt.  Det forutsettes at du har lest og forst?tt kravene til innleverte oppgaver ved Ifi: http://www.ifi.uio.no/studier/studentinfo.html#krav.

Oppgave

I denne obligen skal du lage et kommandostyrt system som behandler informasjon om filmer.  Programmet ditt skal lese inn to datafiler, og gi mulighet til brukeren for ? utf?re kommandoer som finner og viser frem forskjellig type informasjon om filmdataene som ble lest inn.  Programmet ditt trenger ikke skrive til fil eller endre datafilene.

Filen filmdata.txt

Denne filen inneholder informasjon om filmer, med én linje for hver film.  Hver linje har 6 eller flere felt, som er adskilt vha. et tabulator-tegn (dette skilletegnet angis som "\t" i Java):
kode  tittel  ?r  regiss?r  skuespillere;...  sjangre   [evt.tillegg]...
Noen av de siste feltene kan mangle data, og da st?r det "-" i feltet.

Filen persondata.txt

Denne filen har data p? to felt, adskilt med tabulator-tegn: kode, og fullt navn til en person.  Her st?r navnene til alle regiss?rer og skuespillere som var angitt i den andre datafilen med bare kode.  Tips: Disse kodene hopper ikke over noen tall, f.eks. hvis det finnes ole9, s? vil ogs? ole1 til ole8 finnes.  Dette kan du bruke til ? kunne s?ke raskere etter personer (eller filmkoder, som f?lger samme prinsipp) n?r bruker taster 3 bokstaver og ?nsker en liste over matchende personer eller filmer.

P.S. Det er litt feil og mangler i datafilene, men disse p?virker ikke en l?sning av obligen.  Du trenger alts? ikke laste ned evt. senere utgaver av datafilene for ? l?se obligen (men spesielt interesserte kan se om rettelser er lagt ut ved ? se p? fildatoene).

Kommandoene

Oppgaven din blir ? lage et program som kan lese inn disse to filene, og svare p? f?lgende typer kommandoer som bruker kan gi.  Du kan velge hvordan du vil sette opp menyen, men det anbefales f?lgende meny:
Eksempler p? kommandoer du kan taste inn:
.      = Vis statistikk
AAA1   = Vis info om en film
AAA    = Finn film
tom1   = Vis info om en person
tom    = Finn person
90     = Vis info om et ti?r
2009   = Vis info om et ?r (*)
a      = Vis info om en sjanger (*)
?      = Vis meny
q      = Avslutt

Kommando ('?' = meny): _
(*): Kommandoene markert med stjerne er valgfrie ekstra-oppgaver, men det anbefales ? l?se disse ogs?.

Idéen her er at bruker kan taste inn en hvilken som helst av disse typer kommandoer, og programmet finner hva slags kommando ble gitt ut fra antall tegn i kommandoen og om disse tegn er sifre, bokstaver, eller punktum.  Men hvis du foretrekker det kan du ogs? bruke to-stegs kommandoer som i oblig 2 og 3 (menyvalg etterfulgt av sp?rsm?l til bruker).

  1. Vis statistikk: Skal skrive ut totalt antall filmer, og antall filmer i hvert av ti?rene 1980-1989, 1990-1999, og 2000-2009.

  2. Vis info om en film, og Finn film: Skal la brukeren angi en film, og skriver deretter ut f?lgende informasjon om filmen: tittel, ?r, fullt navn til regiss?r, og fullt navn til skuespillerne.  Brukeren skal kunne angi film p? minst 2 forskjellige m?ter:
    • kode: Hvis det som brukeren tastet inn var koden til en av filmene (f.eks. "AVA1" for Avatar), skal info om den filmen skrives ut.
    • s?k: Hvis bruker bare tastet inn tre bokstaver, og det f?rste (eller flere) er store bokstaver, s? skal programmet skrive ut en liste med filmene hvor tittelen (film-koden) begynner med disse bokstavene, og brukeren skal kunne velge ?nsket film fra listen.  Du kan bruke de unike film-kodene i datafilen til dette, disse ser bort fra "The " og "A " i begynnelsen av filmnavn.
    • navn (valgfri ekstra-oppgave) (*): Hvis du ?nsker det kan du ogs? implementere andre m?ter ? angi filmer p?, f.eks. med full tittel til filmen eller full tittel etterfulgt av ?r.  Du kan ogs? gi enda mer informasjon om filmen, f.eks. sjangre eller evt. serie som filmen tilh?rer.

  3. Vis info om en person, og Finn person: Skal fungere omtrent som kommandoen ovenfor, med de samme 2 eller 3 m?ter ? angi ?nsket person p? (men med sm? bokstaver i stedet).  Informasjonen som vises skal inneholde filmene som personen regisserte, og de som hun spilte i.

  4. Vis info om et ti?r: Hvis brukeren taster inn to tallsifre, og det siste er 0, skal programmet vise f?lgende to ting: (a) ?Mest aktiv regiss?r?: Regiss?ren som lagde flest filmer det ti?ret, og (b) Filmene som st?r i 2 eller flere av filmlistene 1-5 i det ti?ret.  Filmlistene er angitt med kodene 1-5 i felt nr. 6 for hver film, f.eks. kode ?2? st?r p? filmer som har f?tt Oscar.

  5. Vis info om et ?r (*): Hvis brukeren taster inn et ?rstall mellom 1900 og 2010 s? skal programmet vise f?lgende to ting om det ?ret: (a) ??rets sjanger? blant comedy, fantasy, horror, eller science-fiction: her skal programmet finne hvilken av disse 4 sjangrene forekommer i flest filmer det ?ret, basert p? sjanger-kodene c, f, h, s. Skriv ogs? ut antall filmer resultatet er baserert p?; og (b) Hvilken film st?r i flest ?reslister (1-5) det ?ret (uten hensyn til sjanger).

  6. Vis info om en sjanger (*): Taster bruker bare en av bokstavene a, E, H, x (som st?r for a=action, E=eventyrfilm, H=superhero, x=disaster), s? skal programmet vise f?lgende to ting om valgt sjanger: (a) Skuespilleren som spilte i flest filmer i sjangeren; og (b) Navnene p? filmseriene som har minst en film innen sjangeren.
(*): Kommandoene markert med stjerne er valgfrie ekstra-oppgaver, men det anbefales ? l?se disse ogs?.

Hint

Disse hint er bare for de som ?nsker litt ekstra-hjelp.  Det kan ogs? bli lagt ut flere ekstra-hint senere, men obligen kan alts? godt l?ses uten ? lese noen av hintene.

  1. Programskall: Her er et eksempel p? et mulig skall for programmet, men du kan l?re mer med obligen hvis du setter opp programstrukturen din p? egen h?nd f?r du ser p? dette eksemplet!
    /* Skriv en kommentar om din besvarelse her.
     * ...
     *
     * Leveringsm?te for UML-diagram: ...
     */
    import easyIO.*;
    import java.util.HashMap;
    
    class Oblig4 {
        public static void main(String[] args) {
            new Filmregister().ordrel?kke();
        }
    }
    
    class Person {
        String kode;
        String navn;
    
        boolean erRegiss?r;
        boolean erSkuespiller;
        String filmerRegissert; // Filmkoder adskilt f.eks. med semikolon.
        String filmerSpilt; // Filmkoder adskilt f.eks. med semikolon.
    
        // filmerRegissert og filmerSpilt kan ogs? deklareres som HashMap-er
        // eller f.eks. overf?res til arrayer av Film[]-pekere n?r alle filmer
        // er lagt inn.  Se hint 3 for flere tips.
    
        // Evt. metoder for ? behandle en person.
    }
    
    class Film {
        // Variabler for dataene som gjelder en film.
        // ...
    
        // Evt. metoder for ? behandle en film.
    }
    
    class TiAar {
        // Variabler for dataene som gjelder et ti?r.
        // ...
    
        // Evt. metoder for ? behandle et ti?r.
    }
    
    class Filmregister {
        In tast = new In();
        Out skjerm = new Out();
    
        HashMap<String, Person> personer = new HashMap<String, Person>();
        HashMap<String, Film> filmer = new HashMap<String, Film>();
        TiAar[] ti?r = new TiAar[11]; // [0]=1900-1909, ..., [10]=2000-2009
    
        /**
          * Konstrukt?r: Leser datafilene, lagrer innholdet i objekter av
          * klassene Person, Film, (og evt. TiAar), og putter Person- og
          * Film-objektene i HashMap-ene ?personer? og ?filmer?.
          */
        Filmregister() {
            // Leser datafilen "persondata.txt":
            In fil = new In("persondata.txt");
            fil.inLine(); // Hopp over f?rste linje, som ikke har data.
            while (! fil.endOfFile()) {
                // Les en linje fra datafilen:
                String kode = fil.inWord();
                String navn = fil.inLine();
    
                // Opprett Person-objekt, og lagre det i HashMap-en ?personer?.
                // ...
    
                skjerm.out(kode.charAt(0)); // Testutskrift.
            }
            fil.close();
    
            // Leser datafilen "filmdata.txt":
            fil = new In("filmdata.txt");
            fil.inLine(); // Hopp over f?rste linje, som ikke har data.
            while (! fil.endOfFile()) {
                // F?lgende setning leser inn en hel linje fra datafilen og
                // oppretter en array med de forskjellige feltene i linjen.
                // felt[0] vil da inneholde filmkoden, felt[1] tittel, osv.
                String linje = fil.inLine();
                String[] felt = linje.split("\t");
    
                // Opprett Film-objekt med de innleste felt-datane, og evt.
                // TiAar-objekt hvis det ikke finnes allerede, og lagre
                // filmobjektet i HashMap-en ?filmer?.
                // ...
    
                // For ? teste om det var flere enn 6 felt i linjen kan
                // du bruke if-setningen: if (felt.length > 6).
    
                skjerm.out(felt[0].charAt(0)); // Testutskrift.
            }
            fil.close();
        }
    
        void ordrel?kke() {
            String ordre = ""; // Kommandoen som bruker taster inn.
            char char0 = '?'; // F?rste tegn i kommandoen.
    
            visMeny();
    
            while (! ordre.equals("q")) {
    
                // Skriv ut ledetekst og les inn en ordre fra tastatur.
                skjerm.out("Kommando ('?' = meny): ");
                ordre = tast.readLine();
    
                int ordreLengde = ordre.trim().length();
                if (ordreLengde > 0) {
                    char0 = ordre.charAt(0);
                }
    
                if (ordreLengde == 1 && char0 == '?') {
                    visMeny();
    
                } else if (ordreLengde >= 3 && char0 >= 'A' && char0 <= 'Z') {
                    visInfoOmFilm(ordre);
    
                } // else if ...osv...
    
                // Skriv en else-if gren for hver ordretype.
            }
        }
    
        void visMeny() {
            // Skriv ut meny her.
            skjerm.outln("...");
            skjerm.outln("?      = Vis meny");
            skjerm.outln("q      = Avslutt");
        }
    
        void visInfoOmFilm(String kode) {
            // Vis info om filmen som har angitt kode (inn-parameter).
        }
    
        // Lag en metode for hver ordre her.  Disse metodene kan
        // kalle p? metoder i de andre klassene.
    }
    

  2. UML-klassediagram: Se eksempel p? side 236 i l?reboka.  Du kan bruke nesten et hvilket som helst tegneprogram for ? lage diagrammet p? datamaskin (hvis du vil levere det via Joly eller mail), eller du kan scanne inn en papir-tegning.  Det er scanner p? Abel- og VB-termstuen.  Hvis du velger elektronisk levering, skal du bruke en av disse filtypene: .pdf, .png, .gif, .jpg, .txt.

  3. String contains og split: For ? teste om en film tilh?rer en sjanger kan du bruke den forh?ndsdefinerte metoden ?contains? for tekster, f.eks. hvis ?sjangre? er en String-variabel med det som sto i felt 6 for filmen ?film?, s? vil f?lgende if-setning teste om filmen er en action-film:
    if (film.sjangre.contains("a")) { // ...
    
    Tilsvarende kan du teste for koder p? mer enn ett tegn, men det fungerer best hvis teksten man skal lete inn i har et skilletegn p? slutten av koden man ser etter (eller b?de foran og bak), f.eks. hvis du har valgt ? lagre skuespillerlista til en film i en String-variabel ?stars?, og plusset p? skilletegn bak (stars = stars + ";";), s? kan du teste om Sandra Bullock spiller i filmen slik:
    if (film.stars.contains("san1;")) { // ...
    
    Dette gjelder hvis du lagrer skuespillerlisten til en film i en String-variabel.  To andre m?ter ? lagre slike sm? lister p? er som en array (som kan opprettes vha. stars.split(";")) eller i en liten HashMap.  Du kan se et eksempel p? split i koden ovenfor.  NB! Tekst-verdien du bruker split p? skal ikke inneholde skilletegnet helt foran, s? hvis teksten har ";" som f?rste tegn b?r dette fjernes (f.eks. vha. substring) f?r man utf?rer split(";"), hvis ikke kan man f? en tom streng som f?rste resultat av split.

  4. keySet og substring: Du vil f? bruk for metodene for manipulasjon av HashMap-er og String-er, disse kan du lese mer om i kapittel 6 og 9 i l?reboka, les bl.a. om substring, indexOf, startsWith, parseInt, og split p? side 104-111 om tekster; og om put, get, keySet, values, size, og containsKey p? side 180-189 om HashMap-er.

  5. Store og sm? bokstaver: Du kan teste om et tegn i ordren som brukeren tastet inn er stor eller liten bokstav p? mange m?ter, her er tre alternative m?ter, velg en av dem:
    if (tegn >= 'A' && tegn <= 'Z') { // ...
    if (Character.isUpperCase(tegn)) { // ...
    if (ordre.toUpperCase().equals(ordre)) { // ...
    
    Den f?rste m?ten er ogs? vist i siste else-if i koden ovenfor og g?r ut p? ? teste om et gitt tegn er mellom 'A' og 'Z', i s? fall vet vi at det er en stor bokstav.  Bruk 'a' og 'z' for ? teste for sm? bokstaver.  Neste if-setning viser bruk av den forh?ndsdefinerte metoden isUpperCase(tegn) til klassen Character, denne klassen er alltid tilgjengelig i Java, p? samme m?te som String.  Bruk isLowerCase(tegn) for ? teste for sm? bokstaver.  Siste alternativ viser hvordan du kan teste om alle bokstaver i en ordre er store bokstaver.

  6. Lesing av 6 - 7 felt: Linjene i datafilen "filmdata.txt" kan inneholde 6 eller flere felt.  En enkel m?te ? lese datafilen p? som tar h?yde for dette er vist i koden ovenfor der det st?r split("\t").  Og for ? sjekke om 7. felt er serienavn kan du bruke startsWith("s=").  Det g?r ogs? an ? lese datafilen vha. fil.inWord("\t") og lignende.  Dette leser inn felt som har mellomrom inni seg riktig, men da trenger du ogs? en smart m?te ? skille mellom evt. 7. felt og begynnelsen av neste linje.

Kilder

All data som st?r i datafilene kommer fra Wikipedia.  Film-listen er tatt fra disse alfabetiske listene.

Kommentarer og rettelser til denne obligen kan sendes til josek [at] ifi.uio.no (Jose Luis Rojas K.)