/*
 *  IndexDirect.java  -  Joris van Rantwijk
 *  in2026 Inleiding databeheer
 *
 *  Indexdirecte bestandsorganisatie.
 */

import java.io.RandomAccessFile;
import java.io.IOException;

/*
 *  Er wordt gebruik gemaakt van aparte files voor de index en de records.
 *  De indexfile bestaat uit een vast aantal pagina's. Per pagina is er een
 *  vast aantal (sleutel, adres) paren, waarbij het adres verwijst naar het
 *  record met die sleutel (dense-indexing).
 *  De toegang tot de index gaat via statische hashing; er is geen
 *  voorziening voor overloop. Vrije posities in de indexfile worden
 *  gemarkeerd door een ongeldige sleutelwaarde (-1).
 *
 *  De recordfile bevat een opeenvolging van individuele records (b=1). Vrije
 *  posities in de recordfile (ontstaan door het verwijderen van records)
 *  worden bijgehouden in een aparte file. Deze derde file bevat een 
 *  opsomming van adressen van vrije posities in de recordfile. Wanneer een 
 *  positie weer in gebruik wordt genomen, moet het adres uit de file 
 *  verwijderd worden. Dit gebeurt door het te vervangen door een ongeldig 
 *  adres (-1).
 */


//===========================================================================
// Class IndexDirect
// Implementatie van een indexdirecte bestandsorganisatie
//===========================================================================
public class IndexDirect extends BestandsOrganisatie
{
    // De gebruikte files.
    private RandomAccessFile ifile;
    private RandomAccessFile rfile;
    private RandomAccessFile vfile;

    // Aantal pagina's in de indexfile.
    // Dit is het aantal mogelijke waarden van de hashfunctie.
    private final static int MAXHASH = 100;

    // De omvang van een pagina in bytes.
    private final static int PAGESIZE = 1024;

    // Het aantal entries per pagina van de indexfile.
    // Per entry worden twee integers (sleutel en adres) opgeslagen.
    // Hiervoor zijn 2 * 4 = 8 bytes nodig.
    private int indexEntriesPerPage = PAGESIZE / 8;


    // Construeer een IndexDirect class.
    // Gebruik de gegeven filenaam, en het gegeven recordtype.
    //-----------------------------------------------------------------------
    public IndexDirect(String filenaam, Class recordType) throws IOException
    {
        this.recordType = recordType;
        this.recordLen = Record.newRecord(recordType).getRecordlen();

        // Open de benodigde files. Als de files nog niet bestaan, 
        // worden er automatisch lege files aangemaakt.
        ifile = new RandomAccessFile(filenaam + "i", "rw");
        rfile = new RandomAccessFile(filenaam + "r", "rw");
        vfile = new RandomAccessFile(filenaam + "v", "rw");

        // Controleer de lengte van de indexfile.
        // Als dit de eerste keer is dat deze files geopend worden
        // (dwz de database bestond nog niet), moet een reeks lege
        // pagina's in de indexfile worden geschreven.
        if (ifile.length() == 0) {
            for (int i = 0; i < MAXHASH; i++) {
                ifile.seek(i * PAGESIZE);
                for (int j = 0; j < indexEntriesPerPage; j++) {
                    // Schrijf een ongeldige sleutel en ongeldig adres.
                    ifile.writeInt(-1);
                    ifile.writeInt(-1);
                }
            }
        }
    }


    // Sluit het bestand.
    //-----------------------------------------------------------------------
    public void close() throws IOException
    {
        // Sluit alle files.
        ifile.close();
        rfile.close();
        vfile.close();
    }


    // Bereken de hashwaarde van de gegeven sleutel.
    //-----------------------------------------------------------------------
    private static int hash(int key)
    {
        return (key % MAXHASH);
    }

    
    // Voeg het record r toe.
    //-----------------------------------------------------------------------
    public void insert(Record r) throws Exception, IOException
    {
        int h;
        int i, k, p;
        int key, address;

        // Bereken de hashwaarde van de sleutel.
        h = hash(r.key);

        // Zoek in de bijbehorende indexpagina naar een vrije positie.
        // Controleer tegelijkertijd of het record nog niet aanwezig is.
        ifile.seek(h * PAGESIZE);
        diskaccess++;
        k = -1;
        for (i = 0; i < indexEntriesPerPage; i++) {
            // Lees een (sleutel, adres) paar.
            key = ifile.readInt();
            address = ifile.readInt();
            if (key == -1 && k == -1) {
                // Vrije positie gevonden; onthoud dit in k.
                k = i;
            }
            if (key == r.key) {
                // Record met zelfde sleutel gevonden; geef een exceptie.
                throw new Exception("Record bestaat al");
            }
        }

        if (k == -1) {
            // Er is geen ruimte in de indexpagina.
            // We hebben geen voorziening voor overloop dus ...
            throw new Exception("Indexpagina vol");
        }

        // Zoek een plekje in de recordfile voor het nieuwe record.
        // Doorloop eerst de vfile om een vrije positie te zoeken.
        p = -1;
        vfile.seek(0);
        for (i = 0; i < vfile.length() / 4; i++) {
            p = vfile.readInt();
            if (p != -1) break;
        }

        if (p != -1) {
            // Vrije positie gevonden.
            // Verwijder deze vrije positie uit de vfile.
            vfile.seek(i * 4);
            vfile.writeInt(-1);
        } else {
            // Geen vrije positie gevonden.
            // Voeg dan het record toe aan het eind van de recordfile.
            p = (int) (rfile.length() / recordLen);
        }

        // Schrijf het nieuwe record in de recordfile.
        rfile.seek(p * recordLen);
        r.write(rfile);
        diskaccess++;

        // Voeg de sleutel en het adres van het nieuwe record toe aan de 
        // index. Gebruik daarvoor de vrije indexpositie in k.
        ifile.seek(h * PAGESIZE + k * 8);
        ifile.writeInt(r.key);
        ifile.writeInt(p);
        diskaccess++;
    }


    // Vervang een bestaand record door r.
    //-----------------------------------------------------------------------
    public void update(Record r) throws Exception, IOException
    {
        int h, i;
        int key, address;

        // Bereken de hashwaarde van de sleutel.
        h = hash(r.key);

        // Zoek in de bijbehorende indexpagina naar het record.
        ifile.seek(h * PAGESIZE);
        diskaccess++;
        for (i = 0; i < indexEntriesPerPage; i++) {
            // Lees een (sleutel, adres) paar.
            key = ifile.readInt();
            address = ifile.readInt();
            if (key == r.key) {
                // We hebben het adres van het record gevonden.
                // Schrijf nu in het recordfile het nieuwe record 
                // over het oude heen.
                rfile.seek(address * recordLen);
                r.write(rfile);
                diskaccess++;
                // Klaar met de update operatie.
                return;
            }
        }

        // Het record bestaat niet; geef een exceptie.
        throw new Exception("Record niet gevonden");
    }

    
    // Zoek het record met de sleutel key.
    //-----------------------------------------------------------------------
    public Record retrieve(int key) throws IOException
    {
        Record r = Record.newRecord(recordType);
        int h, i;
        int k, address;

        // Bereken de hashwaarde van de sleutel.
        h = hash(key);

        // Zoek in de bijbehorende indexpagina naar het record.
        ifile.seek(h * PAGESIZE);
        diskaccess++;
        for (i = 0; i < indexEntriesPerPage; i++) {
            // Lees een (sleutel, adres) paar.
            k = ifile.readInt();
            address = ifile.readInt();
            if (k == key) {
                // We hebben het adres van het record gevonden.
                // Lees het record uit het recordfile.
                rfile.seek(address * recordLen);
                r.read(rfile);
                diskaccess++;
                // Geef het record terug.
                return r;
            }
        }

        // Het record bestaat niet; geef null terug.
        return null;
    }

    
    // Verwijder het record met de sleutel <key>.
    //-----------------------------------------------------------------------
    public void delete(int key) throws Exception, IOException
    {
        Record tr;
        int h, i;
        int p, k, address;

        // Bereken de hashwaarde van de sleutel.
        h = hash(key);

        // Zoek in de bijbehorende indexpagina naar het record.
        k = -1;
        address = -1;
        ifile.seek(h * PAGESIZE);
        diskaccess++;
        for (i = 0; i < indexEntriesPerPage; i++) {
            // Lees een (sleutel, adres) paar.
            k = ifile.readInt();
            address = ifile.readInt();
            if (k == key) break;
        }

        if (k != key) {
            // Het record is niet gevonden; geef een exceptie.
            throw new Exception("Record niet gevonden");
        }

        // We hebben de indexentry van het record gevonden.
        // Verwijder het record uit de index.
        ifile.seek(h * PAGESIZE + i * 8);
        ifile.writeInt(-1);
        ifile.writeInt(-1);
        diskaccess++;

        // Verwijder het record uit de recordfile door er een record
        // met een ongeldig adres overheen te schrijven.
        tr = Record.newRecord(recordType);
        rfile.seek(address * recordLen);
        tr.write(rfile);
        diskaccess++;

        // Neem de vrijgekomen positie in de recordfile op in de lijst van
        // vrije recordposities. Zoek hiervoor een plekje in de vfile.
        vfile.seek(0);
        for (i = 0; i < vfile.length() / 4; i++) {
            p = vfile.readInt();
            if (p == -1) {
                // Vrije plek in de vfile gevonden.
                // Schrijf hier straks het adres.
                vfile.seek(i * 4);
                break;
            }
        }
        // Schrijf het adres naar de vfile.
        vfile.writeInt(address);
    }

}

//===========================================================================
// End
//===========================================================================

Generated by Java2Html