Oblig 4 (INF1000 - H?st 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, i tillegg til HashMap.

Leveringsfrist

Fredag 12. nov kl. 16.00.  Leveres i dette Innleveringssystemet.  Husk ? velge innleveringsnummer 4.

Leveringskrav

L?sningen 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.  Det skal leveres to ting: Hvis du har sp?rsm?l om obligen kan du skrive de i kurs-bloggen.  Der vil det bli lagt ut svar og evt. andre tips og presiseringer til obligen.  Du kan ogs? f? hjelp p? gruppene, under oraklene, eller p? Studielaben.

Samarbeid to-og-to

Du kan velge om du vil l?se obligen alene eller sammen med én annen student i kurset.  Hvis du skal l?se den alene gjelder de samme retningslinjene som nevnt tidligere i Oblig 2 og 3, og Kravene til innleverte oppgaver ved Ifi: http://www.ifi.uio.no/studier/studentinfo.html#krav. Hvis du vil jobbe i en gruppe p? to studenter m? dere oppfylle disse tre tilleggskrav:
  1. Bare én student skal levere besvarelsen.  Denne leveres i innleveringssystemet p? vanlig m?te.  Husk ? velge innleveringsnr. 4.  Det blir gruppel?reren i gruppen til denne studenten som retter obligen, og det er derfor ogs? til ham dere skal levere UML-klassediagrammet deres.
  2. Besvarelsen (.java-filen) m? ha en kommentar ?verst der dere skriver at besvarelsen gjelder for to studenter, med brukernavn og gruppenummer til begge to.
  3. Den andre studenten m? sende en mail til sin gruppel?rer ? se mailadresse her.  I mailen skriver du brukernavn og gruppenummer til begge studentene i 欧洲杯在线买球_欧洲杯投注网站推荐@sgruppen.  (Ikke send besvarelsen!, den skal som sagt bare leveres av én student i 欧洲杯在线买球_欧洲杯投注网站推荐@sgruppen).  Fristen for ? sende denne mailen er den samme som for ? levere obligen.  "Subject"-feltet i mailen b?r v?re: Samarbeid INF1000 Oblig 4
Gruppel?reren som retter obligen vil sende tilbakemelding til begge studenter p? mail, og ogs? til gruppel?reren til den andre studenten hvis studentene er i forskjellige grupper.

Oppgave

I denne obligen skal du lage et kommandostyrt system som behandler informasjon om filmer.  Programmet skal lese inn data fra f?lgende to datafiler, og gi brukeren mulighet til ? utf?re kommandoer som finner og viser frem forskjellig type informasjon om filmene.  Programmet trenger ikke skrive til fil eller endre datafilene, og du skal ikke levere disse filene.

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 ved hjelp av 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.

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:
m      = Vis meny
s      = Vis statistikk
AAA1   = Vis info om en film
AAA    = Finn film
tom1   = Vis info om en person
tom    = Finn person
1990s  = Vis info om et ti?r
2009   = [ Ekstraoppgave: Vis info om et ?r ]
:a     = [ Ekstraoppgave: Vis info om en sjanger ]
q      = Avslutt

Kommando ('m' = meny): _

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 andre tegn.

  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:

    • (a) kode: Hvis det som brukeren tastet inn var koden til en av filmene (f.eks. "AVA1" for Avatar), skal programmet ditt skrive ut info om denne filmen.
    • (b) 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 film-koden (og dermed ogs? tittelen) 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.
    • (c) navn [ ekstraoppgave ]: 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 mellomrom og ?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, med tittel og ?r.

  4. Vis info om et ti?r: Hvis brukeren taster inn fire tallsifre etterfulgt av bokstaven s (f.eks. 1990s), og siste tallsiffer er 0, s? skal programmet vise f?lgende to ting: (a) ?Mest aktiv regiss?r?: Regiss?ren som lagde flest filmer det ti?ret; og (b) ?Beste filmer?: 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. [ Ekstraoppgave: 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 film?: Hvilken film st?r i flest ?reslister (1-5) det ?ret; og (b) ??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?.

  6. [ Ekstraoppgave: Vis info om en sjanger: ] Taster bruker kolon etterfulgt av 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.


Hint

Disse hint er ment for de som ?nsker litt ekstra-hjelp.  Du trenger ikke lese dette avsnittet for ? l?se obligen.  Du kan ogs? sp?rre om og finne flere hint i bloggen.  Det kan v?re lurt ? l?se litt enklere oppgaver f?r du g?r l?s p? deloppgavene i denne obligen, s?rlig Ukeoppgaver 9 om HashMap (som er hovedtemaet i obligen).  Oppgavesettet har l?sningsforslag.
  1. Programskall:  Her er et eksempel p? et mulig skall for programmet, men du kan sannsynligvis l?re mer med obligen hvis du setter opp programstrukturen din p? egen h?nd f?r du studerer dette eksemplet!

    /* Skriv en kommentar om din besvarelse her.
     * ...
     *
     * Leveringsm?te for UML-klassediagram: ...
     */
    import easyIO.*;
    import java.util.HashMap;
    
    class Oblig4 {
        public static void main(String[] args) {
            new Filmregister().ordrelokke();
        }
    }
    
    class Person {
        String kode;
        String navn;
    
        boolean erRegissor;
        boolean erSkuespiller;
        String filmerRegissert; // Filmkoder adskilt f.eks. med semikolon.
        String filmerSpilt; // Filmkoder adskilt f.eks. med semikolon.
    
        // filmerRegissert og filmerSpilt kan ogs? overf?res til arrayer av
        // Film[]-pekere n?r alle filmer er lagt inn; eller du kan lagre de
        // i sm? HashMap-er.  Se hint 3 for flere tips.
    
        // Evt. metoder for ? behandle en person.
    }
    
    class Film {
        // Variabler for dataene som gjelder for en film.
        // ...
    
        // Evt. metoder for ? behandle en film.
    }
    
    class TiAar {
        // Variabler for dataene som gjelder for 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[] tiaar = 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 ordrelokke() {
            String ordre = ""; // Kommandoen som bruker taster inn.
            char char0 = 'm'; // F?rste tegn i kommandoen.
    
            visMeny();
    
            while (! ordre.equals("q")) {
    
                // Skriv ut ledetekst og les inn en ordre fra tastatur.
                skjerm.out("Kommando ('m' = meny): ");
                ordre = tast.readLine();
    
                int ordreLengde = ordre.trim().length();
                if (ordreLengde > 0) {
                    char0 = ordre.charAt(0);
                }
    
                if (ordreLengde == 1 && char0 == 'm') {
                    visMeny();
    
                } else if (ordreLengde >= 3 && char0 >= 'A' && char0 <= 'Z') {
                    visInfoOmFilm(ordre);
    
                } // else if ...osv...
    
                // Skriv en else-if gren for hver ordretype.
            }
        }
    
        void visMeny() {
            // Legg til de andre linjene av menyen her.
            skjerm.outln("\nMeny:");
            skjerm.outln("m      = 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 innleveringssystemet 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, .jpg, .png, .gif, (eller .txt).  UML-klassediagrammer ble gjennomg?tt i Ukeoppgaver 7.


  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 du legger til et skilletegn p? slutten av teksten man skal lete i, 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:  Det er mange m?ter ? teste om et tegn i ordren som brukeren tastet inn er stor eller liten bokstav.  Her er tre mulige 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. Tall eller bokstav:  Det er ogs? mange m?ter ? finne ut om et gitt tegn er et tallsiffer:

    if (tegn >= '0' && tegn <= '9') { // ...
    if (Character.isDigit(tegn)) { // ...
    if (ordre.matches("[0-9]+")) { // ...
    
    Siste alternativet tester om hele ordren best?r av bare tallsifre ("[0-9]" matcher et hvilket som helst tall, og "+"-tegnet betyr ?en eller flere forekomster?).  Det fins ogs? Character.isLetter(tegn) for ? teste om et tegn er en bokstav.



  7. Er ordren et ti?r? (NB: Dette er ikke pensum).  Du kan ogs? bruke f?lgende til ? teste om bruker ga ordren om ti?r:

    if (ordre.matches("[0-9][0-9][0-9]0s")) { // ...
    
    Spesielt interesserte kan lese mer om dette i Java API-en under String.matches(regex) og regular expression (se ogs? side 112 i l?reboka).

  8. Lesing av 6 eller flere felt:  Linjene i datafilen "filmdata.txt" kan inneholde 6 eller flere felt adskilt med tabulator-tegnet.  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 if (felt[6].startsWith("s="))


  9. String til int og tilbake:  Man kan konvertere fra String til int ved hjelp av parseInt slik:

    int tall = Integer.parseInt(tekstVerdi);
    
    Her skal tekstVerdi v?re en String som bare inneholder tallsifre.  Bruk tekstVerdi.substring(f_o_m, til) for ? plukke ut ?nsket del-streng hvis tallsifrene er blandet med andre tegn i tekstVerdi.  Du kan lese mer om substring p? side 103-104 i l?reboka, eller i Java API-en under String.

    Det omvendte, konvertering fra int til String, kan ordnes ved ? plusse p? den tomme strengen:

    String tekst = "" + intVerdi;
    


  10. .tgz og .zip:  F?r du leverer b?r du pakke inn Oblig4.java i en .tgz- eller .zip-fil, sammen med evt. UML-diagram hvis du skal levere diagrammet elektronisk.  Hvis filene heter "Oblig4.java" og "uml.jpg" s? kan du lage en .tgz-fil med begge kalt "Oblig4.tgz" ved hjelp av f?lgende kommando p? Linux:

    tar cvzf Oblig4.tgz Oblig4.java uml.jpg
    
    Og hvis du foretrekker ? lage .zip-fil, kan du bruke f?lgende kommando p? Linux:
    zip -r Oblig4.zip Oblig4.java uml.jpg
    


Kilder

Alle dataene som st?r i datafilene er tatt fra Wikipedia og TMDb (disse er valgt fordi de er ?pne og krever ikke lisens for bruk).  Filmlisten er tatt fra de alfabetiske listene over "filmer som har egen Wikipedia-side". 

Hvis du har sp?rsm?l, kommentarer, eller rettelser til obligen kan du skrive de i kurs-bloggen eller maile de til meg, josek [at] ifi.uio.no..